diff --git a/include/ebcl/RefCount.hh b/include/ebcl/RefCount.hh index fe48ec6..64054a0 100644 --- a/include/ebcl/RefCount.hh +++ b/include/ebcl/RefCount.hh @@ -73,6 +73,9 @@ class T_RcPtr T_AtomicRcClass , T_SimpleRcClass >; + // All RcPtr's are friends + template< typename > friend class T_RcPtr; + private: T_Target* target_; void setTarget_( T_Target* t ) noexcept; @@ -88,6 +91,10 @@ class T_RcPtr template< typename... AT > static T_RcPtr< Type > New( AT&& ... arguments ); + // Initialise from raw pointer. The instance must have a reference + // count of zero, and must have been allocated using new. + static T_RcPtr< Type > FromRaw( Type* ptr ) noexcept; + void clear( ) noexcept; // --------------------------------------------------------------------- @@ -145,6 +152,17 @@ class T_RcPtr template< typename T > void swap( T_RcPtr< T >& lhs , T_RcPtr< T >& rhs ) noexcept; +template< typename T > +inline T_RcPtr< T > T_RcPtr< T >::FromRaw( + T* ptr ) noexcept +{ + assert( ptr->getReferenceCount( ) == 0 ); + T_RcPtr< T > rv; + ptr->increaseReferences_( ); + rv.target_ = ptr; + return rv; +} + } // namespace #endif // _H_EBCL_REFCOUNT diff --git a/include/ebcl/inline/RefCount.hh b/include/ebcl/inline/RefCount.hh index a58ddbc..a10d80f 100644 --- a/include/ebcl/inline/RefCount.hh +++ b/include/ebcl/inline/RefCount.hh @@ -168,7 +168,9 @@ template< T_RcPtr< Q >&& other ) noexcept : target_{ nullptr } { - std::swap( target_ , other.target_ ); + T* const ptr{ (T*) other.target_ }; + other.target_ = nullptr; + target_ = ptr; } template< typename T > diff --git a/tests/ref-count.cc b/tests/ref-count.cc index 740b32f..41ab6a4 100644 --- a/tests/ref-count.cc +++ b/tests/ref-count.cc @@ -12,10 +12,13 @@ class RefCountTest : public CppUnit::TestFixture static uint32_t cNew; static uint32_t cDel; T_Test( ) noexcept { cNew ++; } - ~T_Test( ) noexcept { cDel ++; } + virtual ~T_Test( ) noexcept { cDel ++; } }; using P_Test = T_RcPtr< T_Test >; + class T_Child : public T_Test { }; + using P_Child = T_RcPtr< T_Child >; + private: CPPUNIT_TEST_SUITE( RefCountTest ); CPPUNIT_TEST( testStackInstance ); @@ -24,9 +27,24 @@ class RefCountTest : public CppUnit::TestFixture CPPUNIT_TEST( testConstruct ); CPPUNIT_TEST( testCopyCons ); + CPPUNIT_TEST( testCopyConsToBase ); + CPPUNIT_TEST( testCopyConsToChildOk ); + CPPUNIT_TEST( testCopyConsToChildBad ); + CPPUNIT_TEST( testCopyAss ); + CPPUNIT_TEST( testCopyAssToBase ); + CPPUNIT_TEST( testCopyAssToChildOk ); + CPPUNIT_TEST( testCopyAssToChildBad ); + CPPUNIT_TEST( testMoveCons ); + CPPUNIT_TEST( testMoveConsToBase ); + CPPUNIT_TEST( testMoveConsToChildOk ); + CPPUNIT_TEST( testMoveConsToChildBad ); + CPPUNIT_TEST( testMoveAss ); + CPPUNIT_TEST( testMoveAssToBase ); + CPPUNIT_TEST( testMoveAssToChildOk ); + CPPUNIT_TEST( testMoveAssToChildBad ); CPPUNIT_TEST( testSwap ); CPPUNIT_TEST_SUITE_END( ); @@ -40,9 +58,24 @@ class RefCountTest : public CppUnit::TestFixture void testConstruct( ); void testCopyCons( ); + void testCopyConsToBase( ); + void testCopyConsToChildOk( ); + void testCopyConsToChildBad( ); + void testCopyAss( ); + void testCopyAssToBase( ); + void testCopyAssToChildOk( ); + void testCopyAssToChildBad( ); + void testMoveCons( ); + void testMoveConsToBase( ); + void testMoveConsToChildOk( ); + void testMoveConsToChildBad( ); + void testMoveAss( ); + void testMoveAssToBase( ); + void testMoveAssToChildOk( ); + void testMoveAssToChildBad( ); void testSwap( ); }; @@ -127,6 +160,38 @@ void RefCountTest< A >::testCopyCons( ) CPPUNIT_ASSERT_EQUAL( 1u , T_Test::cDel ); } +template< bool A > +void RefCountTest< A >::testCopyConsToBase( ) +{ + P_Child p1{ P_Child::New( ) }; + P_Test p2{ p1 }; + + CPPUNIT_ASSERT( bool( p2 ) ); + CPPUNIT_ASSERT( p1.get( ) == p2.get( ) ); + CPPUNIT_ASSERT_EQUAL( 2u , p1->getReferenceCount( ) ); +} + +template< bool A > +void RefCountTest< A >::testCopyConsToChildOk( ) +{ + P_Test p1{ P_Test::FromRaw( new T_Child( ) ) }; + P_Child p2{ p1 }; + + CPPUNIT_ASSERT( bool( p2 ) ); + CPPUNIT_ASSERT( p1.get( ) == p2.get( ) ); + CPPUNIT_ASSERT_EQUAL( 2u , p1->getReferenceCount( ) ); +} + +template< bool A > +void RefCountTest< A >::testCopyConsToChildBad( ) +{ + P_Test p1{ P_Test::New( ) }; + CPPUNIT_ASSERT_THROW( P_Child p2{ p1 } , std::bad_cast ); + CPPUNIT_ASSERT_EQUAL( 1u , p1->getReferenceCount( ) ); +} + +/*----------------------------------------------------------------------------*/ + template< bool A > void RefCountTest< A >::testCopyAss( ) { @@ -147,6 +212,41 @@ void RefCountTest< A >::testCopyAss( ) CPPUNIT_ASSERT_EQUAL( 1u , T_Test::cDel ); } +template< bool A > +void RefCountTest< A >::testCopyAssToBase( ) +{ + P_Child p1{ P_Child::New( ) }; + P_Test p2; + p2 = p1; + + CPPUNIT_ASSERT( bool( p2 ) ); + CPPUNIT_ASSERT( p1.get( ) == p2.get( ) ); + CPPUNIT_ASSERT_EQUAL( 2u , p1->getReferenceCount( ) ); +} + +template< bool A > +void RefCountTest< A >::testCopyAssToChildOk( ) +{ + P_Test p1{ P_Test::FromRaw( new T_Child( ) ) }; + P_Child p2; + p2 = p1; + + CPPUNIT_ASSERT( bool( p2 ) ); + CPPUNIT_ASSERT( p1.get( ) == p2.get( ) ); + CPPUNIT_ASSERT_EQUAL( 2u , p1->getReferenceCount( ) ); +} + +template< bool A > +void RefCountTest< A >::testCopyAssToChildBad( ) +{ + P_Test p1{ P_Test::New( ) }; + P_Child p2; + CPPUNIT_ASSERT_THROW( p2 = p1 , std::bad_cast ); + CPPUNIT_ASSERT_EQUAL( 1u , p1->getReferenceCount( ) ); +} + +/*----------------------------------------------------------------------------*/ + template< bool A > void RefCountTest< A >::testMoveCons( ) { @@ -162,6 +262,39 @@ void RefCountTest< A >::testMoveCons( ) CPPUNIT_ASSERT_EQUAL( 1u , T_Test::cDel ); } +template< bool A > +void RefCountTest< A >::testMoveConsToBase( ) +{ + P_Child p1{ P_Child::New( ) }; + P_Test p2{ std::move( p1 ) }; + + CPPUNIT_ASSERT( bool( p2 ) ); + CPPUNIT_ASSERT( !p1 ); + CPPUNIT_ASSERT_EQUAL( 1u , p2->getReferenceCount( ) ); +} + +template< bool A > +void RefCountTest< A >::testMoveConsToChildOk( ) +{ + P_Test p1{ P_Test::FromRaw( new T_Child( ) ) }; + P_Child p2{ std::move( p1 ) }; + + CPPUNIT_ASSERT( bool( p2 ) ); + CPPUNIT_ASSERT( !p1 ); + CPPUNIT_ASSERT_EQUAL( 1u , p2->getReferenceCount( ) ); +} + +template< bool A > +void RefCountTest< A >::testMoveConsToChildBad( ) +{ + P_Test p1{ P_Test::New( ) }; + CPPUNIT_ASSERT_THROW( P_Child p2{ std::move( p1 ) } , std::bad_cast ); + CPPUNIT_ASSERT( p1 ); + CPPUNIT_ASSERT_EQUAL( 1u , p1->getReferenceCount( ) ); +} + +/*----------------------------------------------------------------------------*/ + template< bool A > void RefCountTest< A >::testMoveAss( ) { @@ -179,6 +312,40 @@ void RefCountTest< A >::testMoveAss( ) CPPUNIT_ASSERT_EQUAL( 1u , T_Test::cDel ); } +template< bool A > +void RefCountTest< A >::testMoveAssToBase( ) +{ + P_Child p1{ P_Child::New( ) }; + P_Test p2; + p2 = std::move( p1 ); + + CPPUNIT_ASSERT( p2 ); + CPPUNIT_ASSERT( !p1 ); + CPPUNIT_ASSERT_EQUAL( 1u , p2->getReferenceCount( ) ); +} + +template< bool A > +void RefCountTest< A >::testMoveAssToChildOk( ) +{ + P_Test p1{ P_Test::FromRaw( new T_Child( ) ) }; + P_Child p2; + p2 = std::move( p1 ); + + CPPUNIT_ASSERT( p2 ); + CPPUNIT_ASSERT( !p1 ); + CPPUNIT_ASSERT_EQUAL( 1u , p2->getReferenceCount( ) ); +} + +template< bool A > +void RefCountTest< A >::testMoveAssToChildBad( ) +{ + P_Test p1{ P_Test::New( ) }; + P_Child p2; + CPPUNIT_ASSERT_THROW( p2 = std::move( p1 ) , std::bad_cast ); + CPPUNIT_ASSERT( p1 ); + CPPUNIT_ASSERT_EQUAL( 1u , p1->getReferenceCount( ) ); +} + /*----------------------------------------------------------------------------*/ template< bool A >