diff options
author | Yuri Gorshenin <y@maps.me> | 2016-07-04 16:24:47 +0300 |
---|---|---|
committer | Yuri Gorshenin <y@maps.me> | 2016-07-05 12:40:47 +0300 |
commit | 7df300bc547e7fbb845ff3cedf95d27b61a7ad73 (patch) | |
tree | d058fe814917d70bf9cff3e95f4eb11bd6c15c4e /base | |
parent | 36fe4a08508b790a16677465e2e0ed5db70f0414 (diff) |
Minor fixes - get rid of redundant copying.
Diffstat (limited to 'base')
-rw-r--r-- | base/base.pro | 1 | ||||
-rw-r--r-- | base/base_tests/base_tests.pro | 1 | ||||
-rw-r--r-- | base/base_tests/ref_counted_tests.cpp | 78 | ||||
-rw-r--r-- | base/ref_counted.hpp | 107 |
4 files changed, 187 insertions, 0 deletions
diff --git a/base/base.pro b/base/base.pro index 934e3298ac..49052f5b1a 100644 --- a/base/base.pro +++ b/base/base.pro @@ -57,6 +57,7 @@ HEADERS += \ object_tracker.hpp \ observer_list.hpp \ range_iterator.hpp \ + ref_counted.hpp \ regexp.hpp \ rolling_hash.hpp \ scope_guard.hpp \ diff --git a/base/base_tests/base_tests.pro b/base/base_tests/base_tests.pro index c447870577..7624cc052f 100644 --- a/base/base_tests/base_tests.pro +++ b/base/base_tests/base_tests.pro @@ -28,6 +28,7 @@ SOURCES += \ mem_trie_test.cpp \ observer_list_test.cpp \ range_iterator_test.cpp \ + ref_counted_tests.cpp \ regexp_test.cpp \ rolling_hash_test.cpp \ scope_guard_test.cpp \ diff --git a/base/base_tests/ref_counted_tests.cpp b/base/base_tests/ref_counted_tests.cpp new file mode 100644 index 0000000000..de27a7c01f --- /dev/null +++ b/base/base_tests/ref_counted_tests.cpp @@ -0,0 +1,78 @@ +#include "testing/testing.hpp" + +#include "base/ref_counted.hpp" + +using namespace my; + +namespace +{ +struct Resource : public RefCounted +{ + Resource(bool & destroyed) : m_destroyed(destroyed) {} + ~Resource() override { m_destroyed = true; } + + bool & m_destroyed; +}; + +UNIT_TEST(RefCounted_Smoke) +{ + { + RefCountPtr<Resource> p; + } + + { + bool destroyed = false; + { + RefCountPtr<Resource> p(new Resource(destroyed)); + TEST_EQUAL(1, p->NumRefs(), ()); + TEST(!destroyed, ()); + } + TEST(destroyed, ()); + } + + { + bool destroyed = false; + { + RefCountPtr<Resource> a(new Resource(destroyed)); + TEST_EQUAL(1, a->NumRefs(), ()); + TEST(!destroyed, ()); + + RefCountPtr<Resource> b(a); + TEST(a.Get() == b.Get(), ()); + TEST_EQUAL(2, a->NumRefs(), ()); + TEST(!destroyed, ()); + + { + RefCountPtr<Resource> c; + TEST(c.Get() == nullptr, ()); + + c = b; + TEST(a.Get() == b.Get(), ()); + TEST(b.Get() == c.Get(), ()); + TEST_EQUAL(3, a->NumRefs(), ()); + TEST(!destroyed, ()); + } + + TEST(a.Get() == b.Get(), ()); + TEST_EQUAL(2, a->NumRefs(), ()); + TEST(!destroyed, ()); + + RefCountPtr<Resource> d(move(b)); + TEST(b.Get() == nullptr, ()); + TEST(a.Get() == d.Get(), ()); + TEST_EQUAL(2, a->NumRefs(), ()); + TEST(!destroyed, ()); + + a = a; + TEST_EQUAL(a.Get(), d.Get(), ()); + TEST_EQUAL(2, a->NumRefs(), ()); + TEST(!destroyed, ()); + + TEST_EQUAL(a.Get(), d.Get(), ()); + TEST_EQUAL(2, a->NumRefs(), ()); + TEST(!destroyed, ()); + } + TEST(destroyed, ()); + } +} +} // namespace diff --git a/base/ref_counted.hpp b/base/ref_counted.hpp new file mode 100644 index 0000000000..54feffc08b --- /dev/null +++ b/base/ref_counted.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include "base/macros.hpp" + +#include "std/cstdint.hpp" +#include "std/unique_ptr.hpp" + +namespace my +{ +class RefCounted +{ +public: + virtual ~RefCounted() = default; + + inline void IncRef() noexcept { ++m_refs; } + inline uint64_t DecRef() noexcept { return --m_refs; } + inline uint64_t NumRefs() const noexcept { return m_refs; } + +protected: + RefCounted() noexcept = default; + + uint64_t m_refs = 0; + + DISALLOW_COPY_AND_MOVE(RefCounted); +}; + +template <typename T> +class RefCountPtr +{ +public: + RefCountPtr() noexcept = default; + + explicit RefCountPtr(T * p) noexcept : m_p(p) + { + if (m_p) + m_p->IncRef(); + } + + explicit RefCountPtr(unique_ptr<T> p) noexcept : RefCountPtr(p.release()) {} + + RefCountPtr(RefCountPtr const & rhs) { *this = rhs; } + + RefCountPtr(RefCountPtr && rhs) { *this = move(rhs); } + + ~RefCountPtr() { Reset(); } + + RefCountPtr & operator=(unique_ptr<T> p) + { + Reset(); + + m_p = p.release(); + if (m_p) + m_p->IncRef(); + + return *this; + } + + RefCountPtr & operator=(RefCountPtr const & rhs) + { + if (this == &rhs) + return *this; + + Reset(); + m_p = rhs.m_p; + if (m_p) + m_p->IncRef(); + + return *this; + } + + RefCountPtr & operator=(RefCountPtr && rhs) + { + if (this == &rhs) + return *this; + + Reset(); + m_p = rhs.m_p; + rhs.m_p = nullptr; + + return *this; + } + + void Reset() + { + if (!m_p) + return; + + if (m_p->DecRef() == 0) + delete m_p; + m_p = nullptr; + } + + T * Get() noexcept { return m_p; } + T const * Get() const noexcept { return m_p; } + + T & operator*() { return *m_p; } + T const & operator*() const { return *m_p; } + + T * operator->() noexcept { return m_p; } + T const * operator->() const noexcept { return m_p; } + + inline operator bool() const noexcept { return m_p != nullptr; } + +private: + T * m_p = nullptr; +}; +} // namespace my |