From ddaa46867df89eef563413a41aa9bd529d7e93da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Wed, 2 Jan 2019 15:50:21 +0100 Subject: [PATCH] Reference counting helpers - Progress, tests * Assignment copy/move operators * Boolean conversion / ! operator * Content access --- include/ebcl/RefCount.hh | 43 +++++- include/ebcl/inline/RefCount.hh | 155 +++++++++++++++++++-- tests/ref-count.cc | 233 ++++++++++++++++++++++++++++++++ 3 files changed, 419 insertions(+), 12 deletions(-) create mode 100644 tests/ref-count.cc diff --git a/include/ebcl/RefCount.hh b/include/ebcl/RefCount.hh index 9038baf..56dd892 100644 --- a/include/ebcl/RefCount.hh +++ b/include/ebcl/RefCount.hh @@ -41,6 +41,9 @@ class T_ReferenceCountedClass protected: T_ReferenceCountedClass( ) noexcept; + + public: + uint32_t getReferenceCount( ) const noexcept; }; // Shortcuts @@ -72,6 +75,7 @@ class T_RcPtr private: T_Target* target_; + void setTarget_( T_Target* t ) noexcept; public: @@ -80,27 +84,58 @@ class T_RcPtr T_RcPtr( ) noexcept; ~T_RcPtr( ) noexcept; + // Constructor function + template< typename... AT > + static T_RcPtr< Type > New( AT&& ... arguments ); + void clear( ) noexcept; // --------------------------------------------------------------------- - // Copy constructors + // Copy constructors and operators T_RcPtr( T_Self const& other ) noexcept; + T_Self& operator= ( T_Self const& other ) noexcept; + template< typename Q , T_EnableForChild< Type , Q > = true > T_RcPtr( T_RcPtr< Q > const& source ) noexcept; template< typename Q , T_EnableForParent< Type , Q > = false > explicit T_RcPtr( T_RcPtr< Q > const& source ); + template< typename Q , T_EnableForChild< Type , Q > = true > + T_Self& operator= ( T_RcPtr< Q > const& other ) noexcept; + template< typename Q , T_EnableForParent< Type , Q > = false > + T_Self& operator= ( T_RcPtr< Q > const& other ); + // --------------------------------------------------------------------- - // Move constructors + // Move constructors and operators template< typename Q , T_EnableForChild< Type , Q > = true > T_RcPtr( T_RcPtr< Q >&& other ) noexcept; - template< typename Q , T_EnableForParent< Type , Q > = false > explicit T_RcPtr( T_RcPtr< Q >&& other ); -}; + template< typename Q , T_EnableForChild< Type , Q > = true > + T_Self& operator= ( T_RcPtr< Q >&& other ) noexcept; + template< typename Q , T_EnableForParent< Type , Q > = false > + T_Self& operator= ( T_RcPtr< Q >&& other ); + + // --------------------------------------------------------------------- + // Boolean conversion + + operator bool ( ) const noexcept; + bool operator! ( ) const noexcept; + + // --------------------------------------------------------------------- + // Content access + + // Access the actual pointer + Type * get( ) const noexcept; + explicit operator Type* ( ) const noexcept; + + // Access the object; undefined behavior if get() == nullptr. + Type* operator -> ( ) const; + Type& operator * ( ) const; +}; } // namespace diff --git a/include/ebcl/inline/RefCount.hh b/include/ebcl/inline/RefCount.hh index 7850652..97fbd8e 100644 --- a/include/ebcl/inline/RefCount.hh +++ b/include/ebcl/inline/RefCount.hh @@ -27,6 +27,12 @@ inline bool T_ReferenceCountedClass< B >::decreaseReferences_( ) noexcept return ( -- referenceCounter_ ) == 0; } +template< bool B > +inline uint32_t T_ReferenceCountedClass< B >::getReferenceCount( ) const noexcept +{ + return uint32_t{ referenceCounter_ }; +} + /*= T_RcPtr ==================================================================*/ @@ -41,6 +47,30 @@ inline T_RcPtr< T >::~T_RcPtr( ) noexcept clear( ); } +template< typename T > +template< typename... AT > +inline T_RcPtr< T > T_RcPtr< T >::New( + AT&& ... arguments ) +{ + T* const obj{ new T( std::forward< AT >( arguments ) ... ) }; + T_RcPtr< T > ptr; + ptr.setTarget_( obj ); + return ptr; +} + +/*----------------------------------------------------------------------------*/ + +template< typename T > +inline void T_RcPtr< T >::setTarget_( + T_Target* t ) noexcept +{ + assert( target_ == nullptr ); + if ( t ) { + t->increaseReferences_( ); + } + target_ = t; +} + template< typename T > inline void T_RcPtr< T >::clear( ) noexcept { @@ -55,11 +85,9 @@ inline void T_RcPtr< T >::clear( ) noexcept template< typename T > inline T_RcPtr< T >::T_RcPtr( T_RcPtr< T > const& other ) noexcept - : target_{ other.target_ } + : target_{ nullptr } { - if ( target_ ) { - target_->increaseReferences_( ); - } + setTarget_( other.target_ ); } template< typename T > @@ -68,11 +96,9 @@ template< T_EnableForChild< T , Q > > inline T_RcPtr< T >::T_RcPtr( T_RcPtr< Q > const& other ) noexcept - : target_{ other.target_ } + : target_{ nullptr } { - if ( target_ ) { - target_->increaseReferences_( ); - } + setTarget_( other.target_ ); } template< typename T > @@ -93,6 +119,45 @@ template< } } +template< typename T > +inline T_RcPtr< T >& T_RcPtr< T >::operator= ( + T_Self const& other ) noexcept +{ + clear( ); + setTarget_( other.target_ ); + return *this; +} + +template< typename T > +template< + typename Q , + T_EnableForChild< T , Q > +> inline T_RcPtr< T >& T_RcPtr< T >::operator= ( + T_RcPtr< Q > const& other ) noexcept +{ + clear( ); + setTarget_( other.target_ ); + return *this; +} + +template< typename T > +template< + typename Q , + T_EnableForParent< T , Q > +> inline T_RcPtr< T >& T_RcPtr< T >::operator= ( + T_RcPtr< Q > const& other ) +{ + clear( ); + if ( other.target_ ) { + T* const nt{ dynamic_cast< T* >( other.target_ ) }; + if ( nt == nullptr ) { + throw std::bad_cast( ); + } + setTarget_( nt ); + } + return *this; +} + /*----------------------------------------------------------------------------*/ template< typename T > @@ -124,6 +189,80 @@ template< } } +template< typename T > +template< + typename Q , + T_EnableForChild< T , Q > +> inline T_RcPtr< T >& T_RcPtr< T >::operator= ( + T_RcPtr< Q >&& other ) noexcept +{ + clear( ); + target_ = other.target_; + other.target_ = nullptr; + return *this; +} + +template< typename T > +template< + typename Q , + T_EnableForParent< T , Q > +> inline T_RcPtr< T >& T_RcPtr< T >::operator= ( + T_RcPtr< Q >&& other ) +{ + clear( ); + if ( other.target_ ) { + T* const nt{ dynamic_cast< T* >( other.target_ ) }; + if ( nt == nullptr ) { + throw std::bad_cast( ); + } + other.target_ = nullptr; + target_ = nt; + } + return *this; +} + +/*----------------------------------------------------------------------------*/ + +template< typename T > +inline T_RcPtr< T >::operator bool ( ) const noexcept +{ + return target_ != nullptr; +} + +template< typename T > +inline bool T_RcPtr< T >::operator! ( ) const noexcept +{ + return target_ == nullptr; +} + +/*----------------------------------------------------------------------------*/ + +template< typename T > +inline T_RcPtr< T >::operator T* ( ) const noexcept +{ + return target_; +} + +template< typename T > +inline T* T_RcPtr< T >::get( ) const noexcept +{ + return target_; +} + +template< typename T > +inline T* T_RcPtr< T >::operator -> ( ) const +{ + assert( target_ != nullptr ); + return target_; +} + +template< typename T > +inline T& T_RcPtr< T >::operator * ( ) const +{ + assert( target_ != nullptr ); + return *target_; +} + } // namespace ebcl #endif // _H_EBCL_INLINE_REFCOUNT diff --git a/tests/ref-count.cc b/tests/ref-count.cc new file mode 100644 index 0000000..186274d --- /dev/null +++ b/tests/ref-count.cc @@ -0,0 +1,233 @@ +#include +#include +using namespace ebcl; + +class RefCountTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE( RefCountTest ); + CPPUNIT_TEST( testStackInstance ); + + CPPUNIT_TEST( testEmpty ); + CPPUNIT_TEST( testConstruct ); + + CPPUNIT_TEST( testCopyCons ); + CPPUNIT_TEST( testCopyAss ); + CPPUNIT_TEST( testMoveCons ); + CPPUNIT_TEST( testMoveAss ); + CPPUNIT_TEST_SUITE_END( ); + + public: + void setUp( ) override; + + void testStackInstance( ); + + void testEmpty( ); + void testConstruct( ); + + void testCopyCons( ); + void testCopyAss( ); + void testMoveCons( ); + void testMoveAss( ); +}; +CPPUNIT_TEST_SUITE_REGISTRATION( RefCountTest ); + +/*----------------------------------------------------------------------------*/ + +namespace { + +#define M_MKTESTDECL_( CLS , ATOMIC ) \ + class T_ ##CLS : public T_ReferenceCountedClass< ATOMIC > \ + { \ + public: \ + static uint32_t cNew; \ + static uint32_t cDel; \ + T_ ##CLS( ) noexcept { cNew ++; } \ + ~T_ ##CLS( ) noexcept { cDel ++; } \ + }; \ + uint32_t T_ ##CLS::cNew = 0; \ + uint32_t T_ ##CLS::cDel = 0; \ + using P_ ##CLS = T_RcPtr< T_ ##CLS > + +M_MKTESTDECL_( Atomic , true ); +M_MKTESTDECL_( Simple , false ); + +class T_Child : public T_Simple { }; +using P_Child = T_RcPtr< T_Child >; + +} // namespace + +/*----------------------------------------------------------------------------*/ + +void RefCountTest::setUp( ) +{ + T_Atomic::cNew = T_Atomic::cDel = 0; + T_Simple::cNew = T_Simple::cDel = 0; +} + +/*----------------------------------------------------------------------------*/ + +void RefCountTest::testStackInstance( ) +{ + { + T_Atomic t1; + T_Simple t2; + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cNew ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cNew ); + CPPUNIT_ASSERT_EQUAL( 0u , t1.getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 0u , t2.getReferenceCount( ) ); + } + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cDel ); +} + +/*----------------------------------------------------------------------------*/ + +void RefCountTest::testEmpty( ) +{ + { + P_Atomic p1; + P_Simple p2; + CPPUNIT_ASSERT_EQUAL( 0u , T_Atomic::cNew ); + CPPUNIT_ASSERT_EQUAL( 0u , T_Simple::cNew ); + CPPUNIT_ASSERT_EQUAL( false , bool{ p1 } ); + CPPUNIT_ASSERT_EQUAL( false , bool{ p2 } ); + CPPUNIT_ASSERT_EQUAL( true , !p1 ); + CPPUNIT_ASSERT_EQUAL( true , !p2 ); + CPPUNIT_ASSERT_EQUAL( (T_Atomic*) nullptr , p1.get( ) ); + CPPUNIT_ASSERT_EQUAL( (T_Simple*) nullptr , p2.get( ) ); + CPPUNIT_ASSERT_EQUAL( (T_Atomic*) nullptr , (T_Atomic*) p1 ); + CPPUNIT_ASSERT_EQUAL( (T_Simple*) nullptr , (T_Simple*) p2 ); + } + CPPUNIT_ASSERT_EQUAL( 0u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 0u , T_Simple::cDel ); +} + +void RefCountTest::testConstruct( ) +{ + { + P_Atomic p1{ P_Atomic::New( ) }; + P_Simple p2{ P_Simple::New( ) }; + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cNew ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cNew ); + CPPUNIT_ASSERT_EQUAL( true , bool{ p1 } ); + CPPUNIT_ASSERT_EQUAL( true , bool{ p2 } ); + CPPUNIT_ASSERT_EQUAL( false , !p1 ); + CPPUNIT_ASSERT_EQUAL( false , !p2 ); + CPPUNIT_ASSERT( (T_Atomic*) nullptr != p1.get( ) ); + CPPUNIT_ASSERT( (T_Simple*) nullptr != p2.get( ) ); + CPPUNIT_ASSERT( (T_Atomic*) nullptr != (T_Atomic*) p1 ); + CPPUNIT_ASSERT( (T_Simple*) nullptr != (T_Simple*) p2 ); + CPPUNIT_ASSERT_EQUAL( 1u , p1->getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 1u , p2->getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 1u , (*p1).getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 1u , (*p2).getReferenceCount( ) ); + } + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cDel ); +} + +/*----------------------------------------------------------------------------*/ + +void RefCountTest::testCopyCons( ) +{ + { + P_Atomic p1{ P_Atomic::New( ) }; + P_Simple p2{ P_Simple::New( ) }; + { + P_Atomic c1{ p1 }; + P_Simple c2{ p2 }; + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cNew ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cNew ); + CPPUNIT_ASSERT( c1 ); + CPPUNIT_ASSERT( c2 ); + CPPUNIT_ASSERT( p1.get( ) == c1.get( ) ); + CPPUNIT_ASSERT( p2.get( ) == c2.get( ) ); + CPPUNIT_ASSERT_EQUAL( 2u , p1->getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 2u , p2->getReferenceCount( ) ); + } + CPPUNIT_ASSERT_EQUAL( 0u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 0u , T_Simple::cDel ); + CPPUNIT_ASSERT_EQUAL( 1u , p1->getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 1u , p2->getReferenceCount( ) ); + } + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cDel ); +} + +void RefCountTest::testCopyAss( ) +{ + { + P_Atomic p1{ P_Atomic::New( ) }; + P_Simple p2{ P_Simple::New( ) }; + { + P_Atomic c1; + P_Simple c2; + CPPUNIT_ASSERT( !c1 ); + CPPUNIT_ASSERT( !c2 ); + c1 = p1; + c2 = p2; + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cNew ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cNew ); + CPPUNIT_ASSERT( c1 ); + CPPUNIT_ASSERT( c2 ); + CPPUNIT_ASSERT( p1.get( ) == c1.get( ) ); + CPPUNIT_ASSERT( p2.get( ) == c2.get( ) ); + CPPUNIT_ASSERT_EQUAL( 2u , p1->getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 2u , p2->getReferenceCount( ) ); + } + CPPUNIT_ASSERT_EQUAL( 0u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 0u , T_Simple::cDel ); + CPPUNIT_ASSERT_EQUAL( 1u , p1->getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 1u , p2->getReferenceCount( ) ); + } + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cDel ); +} + +void RefCountTest::testMoveCons( ) +{ + P_Atomic p1{ P_Atomic::New( ) }; + P_Simple p2{ P_Simple::New( ) }; + { + P_Atomic c1{ std::move( p1 ) }; + P_Simple c2{ std::move( p2 ) }; + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cNew ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cNew ); + CPPUNIT_ASSERT_EQUAL( 0u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 0u , T_Simple::cDel ); + CPPUNIT_ASSERT( c1 ); + CPPUNIT_ASSERT( c2 ); + CPPUNIT_ASSERT( !p1 ); + CPPUNIT_ASSERT( !p2 ); + CPPUNIT_ASSERT_EQUAL( 1u , c1->getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 1u , c2->getReferenceCount( ) ); + } + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cDel ); +} + +void RefCountTest::testMoveAss( ) +{ + P_Atomic p1{ P_Atomic::New( ) }; + P_Simple p2{ P_Simple::New( ) }; + { + P_Atomic c1; + P_Simple c2; + CPPUNIT_ASSERT( !c1 ); + CPPUNIT_ASSERT( !c2 ); + c1 = std::move( p1 ); + c2 = std::move( p2 ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cNew ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cNew ); + CPPUNIT_ASSERT_EQUAL( 0u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 0u , T_Simple::cDel ); + CPPUNIT_ASSERT( c1 ); + CPPUNIT_ASSERT( c2 ); + CPPUNIT_ASSERT( !p1 ); + CPPUNIT_ASSERT( !p2 ); + CPPUNIT_ASSERT_EQUAL( 1u , c1->getReferenceCount( ) ); + CPPUNIT_ASSERT_EQUAL( 1u , c2->getReferenceCount( ) ); + } + CPPUNIT_ASSERT_EQUAL( 1u , T_Atomic::cDel ); + CPPUNIT_ASSERT_EQUAL( 1u , T_Simple::cDel ); +}