// test_lkptr.cpp (C) 2007 adolfo@di-mare.com /** \file test_lkptr.cpp \brief Test data for class \c lkptr. \author Adolfo Di Mare \date 2008. */ #define lkptr_MERGE_IS_PUBLIC #include "lkptr.h" #include "BUnit.h" // Módulo para prueba unitaria de programas /// Test data for class \c lkptr. template class test_lkptr : public TestCase { int * pInt; public: test_lkptr() : TestCase(), pInt(0) {} bool run(); void swap_4x1(); void ok_1x2(); void verify_3x1(); void boost_test(); void array_vs_lkptr(); void null_3x1(); void inheritance(); void downcast(); void selfReturn(); void multiDestroyInt(); void multiDestroyClass(); void multiDestroyVector(); void test_lkptr_cycle(); private: lkptr foo( lkptr r ); }; /// Runs the tests. template bool test_lkptr::run() { verify_3x1(); boost_test(); array_vs_lkptr(); swap_4x1(); ok_1x2(); null_3x1(); inheritance(); downcast(); selfReturn(); multiDestroyInt(); multiDestroyClass(); multiDestroyVector(); test_lkptr_cycle(); return wasSuccessful(); } /// Tries with 3 pointes to the same value. template void test_lkptr::null_3x1() { {{ // test::null_3x1 lkptr A, B, C; A = B = C; assertTrue( A.ok() ); assertTrue( check_ok(A) ); assertTrue( B.ok() ); assertTrue( check_ok(C) ); assertTrue( C.ok() ); assertTrue( check_ok(C) ); assertTrue( A.use_count() == 3 ); assertTrue( B.use_count() == 3 ); assertTrue( C.use_count() == 3 ); assertTrue( A == B ); assertTrue( B == C ); assertTrue( A.get() == 0 && A.isNull() ); assertTrue( A.use_count() == 3 ); assertTrue( B.use_count() == 3 ); assertTrue( C.use_count() == 3 ); }} } /// Swaps 4 \c lkptr's. template void test_lkptr::swap_4x1() { X* p = new X(); lkptr A ( p ); lkptr B, C, D; assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 1 ); assertTrue( C.use_count() == 1 ); assertTrue( A.get() != 0 && *A == *p); A.swap(A); B.swap(B); assertTrue( A.ok() && B.ok() && C.ok() ); A.swap(B); // *B==*p && A==null && C==null assertTrue( A.ok() && B.ok() && C.ok() ); assertTrue( A.get() == 0 ); assertTrue( B.get() != 0 && *B == *p); assertTrue( C.get() == 0 ); assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 1 ); assertTrue( C.use_count() == 1 ); C.swap(C); B.swap(B); assertTrue( A.ok() && B.ok() && C.ok() ); C.swap(B); // *C==*p && A==null && B==null assertTrue( A.ok() && B.ok() && C.ok() ); assertTrue( A.get() == 0 ); assertTrue( B.get() == 0 ); assertTrue( C.get() != 0 && *C == *p); A = B; // *C==0 && (A==B==null) assertTrue( A.use_count() == 2 ); assertTrue( B.use_count() == 2 ); assertTrue( C.use_count() == 1 ); assertTrue( A.ok() && B.ok() && C.ok() ); B.swap(B); C.swap(C); B.swap(C); // *B==0 && (A==C==null) assertTrue( A.use_count() == 2 ); assertTrue( B.use_count() == 1 ); assertTrue( C.use_count() == 2 ); assertTrue( A.ok() && B.ok() && C.ok() ); assertTrue( A.get() == 0 ); assertTrue( B.get() != 0 && *B == X()); assertTrue( C.get() == 0 ); assertTrue( A.ok() && B.ok() && C.ok() ); C = B; // A==null && (B==C==p) != null assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 2 ); assertTrue( C.use_count() == 2 ); assertTrue( A.get() == 0 ); assertTrue( B.get() != 0 && *B == X()); assertTrue( C.get() != 0 && *C == X()); assertTrue( A.ok() && B.ok() && C.ok() ); C.swap(B); // A==null && (B==C==p) != null assertTrue( B.use_count() == 2 ); assertTrue( C.use_count() == 2 ); assertTrue( B.get() != 0 && *B == X()); assertTrue( C.get() != 0 && *C == X()); assertTrue( A.ok() && B.ok() && C.ok() ); X* q = new X(); C.reset( q ); // A==null && (B==p) != (C==q) assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 1 ); assertTrue( C.use_count() == 1 ); assertTrue( B.get() != 0 && *B == X()); assertTrue( C.get() != 0 && *C == X()); assertTrue( B.get() != C.get() ); assertTrue( A.ok() && B.ok() && C.ok() ); A=B; D=C; // (A===B==p) != (C==D==q) assertTrue( A.use_count() == 2 ); assertTrue( B.use_count() == 2 ); assertTrue( C.use_count() == 2 ); assertTrue( D.use_count() == 2 ); assertTrue( A.get() == B.get() ); assertTrue( C.get() == D.get() ); assertTrue( A.get() != C.get() ); assertTrue( *A == *p && *C == *q ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); A.swap(B); D.swap(C); // (A===B==p) != (C==D==q) assertTrue( A.get() == B.get() ); assertTrue( C.get() == D.get() ); assertTrue( A.get() != C.get() ); assertTrue( A.get() == p && C.get() == q ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); A.swap(C); // (C===B==p) != (A==D==q) assertTrue( C.get() == B.get() ); assertTrue( A.get() == D.get() ); assertTrue( A.get() != C.get() ); assertTrue( A.use_count() == 2 ); assertTrue( B.use_count() == 2 ); assertTrue( C.use_count() == 2 ); assertTrue( D.use_count() == 2 ); assertTrue( *A == *q && *C == *p ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); A = B; // (A==C===B==p) != (D==q) assertTrue( A.use_count() == 3 ); assertTrue( B.use_count() == 3 ); assertTrue( C.use_count() == 3 ); assertTrue( D.use_count() == 1 ); assertTrue( A.get() == B.get() ); assertTrue( B.get() == C.get() ); assertTrue( A.get() != D.get() ); assertTrue( A.get() == p && D.get() == q ); assertTrue( *A == *p && *D == *q ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); D = C; // (A==C===B==D==p) && (q->bye->bye) assertTrue( A.use_count() == 4 ); assertTrue( B.use_count() == 4 ); assertTrue( C.use_count() == 4 ); assertTrue( D.use_count() == 4 ); assertTrue( A.get() == B.get() ); assertTrue( B.get() == C.get() ); assertTrue( C.get() == D.get() ); assertTrue( A.get() == p && A.get() != q ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); A.swap(B); B.swap(C); C.swap(D); D.swap(A); A.swap(A); B.swap(B); C.swap(C); D.swap(D); assertTrue( A.use_count() == 4 ); assertTrue( B.use_count() == 4 ); assertTrue( C.use_count() == 4 ); assertTrue( D.use_count() == 4 ); assertTrue( A.get() == B.get() ); assertTrue( B.get() == C.get() ); assertTrue( C.get() == D.get() ); assertTrue( A.get() == p && A.get() != q ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); D.release(); // (A==C===B==p) && (D==0) assertTrue( A.use_count() == 3 ); assertTrue( B.use_count() == 3 ); assertTrue( C.use_count() == 3 ); assertTrue( D.use_count() == 1 ); assertTrue( A.get() == B.get() ); assertTrue( B.get() == C.get() ); assertTrue( C.get() != D.get() ); assertTrue( A.get() == p && A.get() != q ); assertTrue( D.get() == 0 ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); D = A; // (A==C==B==D==p) A.swap(A); B.swap(B); C.swap(C); D.swap(D); assertTrue( A.use_count() == 4 ); assertTrue( B.use_count() == 4 ); assertTrue( C.use_count() == 4 ); assertTrue( D.use_count() == 4 ); assertTrue( A.get() == B.get() ); assertTrue( B.get() == C.get() ); assertTrue( C.get() == D.get() ); assertTrue( A.get() == p && A.get() != q ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); D.release(); // (A==C==B==p) && (D==0) D.swap(A); // (D==C==B==p) && (A==0) assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 3 ); assertTrue( C.use_count() == 3 ); assertTrue( D.use_count() == 3 ); assertTrue( A.get() != B.get() ); assertTrue( B.get() == C.get() ); assertTrue( C.get() == D.get() ); assertTrue( D.get() == p && D.get() != q ); assertTrue( A.get() == 0 ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); C.release(); // (D==B==p) && (A==0) && (C==0) assertTrue( A.isNull() && C.isNull() ); assertTrue( A.get() == C.get() ); assertTrue( B.get() == D.get() ); assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 2 ); assertTrue( C.use_count() == 1 ); assertTrue( D.use_count() == 2 ); D.swap(D); B.swap(B); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); C.swap(B); // (C==D==p) && (A==0) && (B==0) assertTrue( A.isNull() && B.isNull() ); assertTrue( A.get() == B.get() ); assertTrue( C.get() == D.get() ); assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 1 ); assertTrue( C.use_count() == 2 ); assertTrue( D.use_count() == 2 ); D.swap(D); C.swap(C); A.swap(A); C.swap(D); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); B = C; // (B==C==D==p) && (A==0) assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 3 ); assertTrue( C.use_count() == 3 ); assertTrue( D.use_count() == 3 ); assertTrue( B.get() == C.get() ); assertTrue( C.get() == D.get() ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); A.swap(B); // (A==C==D==p) && (B==0) assertTrue( A.use_count() == 3 ); assertTrue( B.use_count() == 1 ); assertTrue( C.use_count() == 3 ); assertTrue( D.use_count() == 3 ); assertTrue( A.get() == C.get() ); assertTrue( C.get() == D.get() ); assertTrue( B.isNull() ); assertTrue( A.ok() && B.ok() && C.ok() && D.ok() ); } template void test_lkptr::ok_1x2() { #if 0 [[ A ]] +-------+-------+ | | | ./-->| [**] | [**] |<--\. ./ | // | \\ | \. | +-//----+----\\-+ | | // ^ ^ \\ | | // | | \\ | | || | | || | | || | | || | | \/ / \ \/ | +--|---+---|--+ +--|---+---|--+ | | | | | | | | | | | [**] | [**] | | [**] | [**] | | | | | | | +------+------+ +------+------+ [[ B ]] [[ C ]] #endif lkptr A, B, C; A.b.prev = &B.b; A.b.next = &C.b; B.b.prev = &A.b; C.b.prev = &A.b; B.b.next = &A.b; C.b.next = &A.b; assertTrue( ! A.ok() ); assertTrue( ! B.ok() ); assertTrue( ! C.ok() ); } /// Tries with 3 pointes to the same value. template void test_lkptr::verify_3x1() { lkptr A ( new X() ); // A = new X(); won't compile ("explicit" constructor) lkptr B, C; B = A; assertTrue( A.use_count() == 2 ); assertTrue( A.use_count() != 1 ); C = B; assertTrue( B.use_count() == 3 ); assertTrue( C.use_count() != 2 ); assertTrue( A.ok() ); assertTrue( check_ok(A) ); assertTrue( B.ok() ); assertTrue( check_ok(C) ); assertTrue( C.ok() ); assertTrue( check_ok(C) ); assertTrue( A.use_count() == 3 ); assertTrue( B.use_count() == 3 ); assertTrue( C.use_count() == 3 ); assertTrue( A.get() == B.get() ); assertTrue( A == B ); assertTrue( B == C ); assertTrue( A.get() == C.get() ); A.release(); assertTrue( A.use_count() == 1 ); assertTrue( B.use_count() == 2 ); assertTrue( C.use_count() == 2 ); assertTrue( B.get() == C.get() ); assertTrue( A != C ); assertTrue( A.get() == 0 ); B.release(); assertTrue( A.get() == B.get() ); assertTrue( C.get() != 0 ); } #if 0 class UDT { long value_; public: static int UDT_use_count; // independent of pointer maintained counts explicit UDT( long value=0 ) : value_(value) { ++UDT_use_count; } ~UDT() { --UDT_use_count; // std::cout << "UDT with value " << value_ << " being destroyed\n"; } long value() const { return value_; } void value( long v ) { value_ = v;; } }; // UDT int UDT::UDT_use_count = 0; #else #include "figures.h" #endif #include /// \brief http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/test/smart_ptr_test.cpp template void test_lkptr::boost_test() { #define ck(v1,v2) { BOOST_TEST( *v1 == v2 ); } #define BOOST_TEST(x) assertTrue(x) #define boost #define scoped_ptr lkptr #define shared_ptr lkptr #define scoped_array array_lkptr #define shared_array array_lkptr BOOST_TEST( UDT::UDT_use_count == 0 ); // reality check // test scoped_ptr with a built-in type long * lp = new long; boost::scoped_ptr sp ( lp ); BOOST_TEST( sp.get() == lp ); BOOST_TEST( lp == sp.get() ); BOOST_TEST( &*sp == lp ); *sp = 1234568901L; BOOST_TEST( *sp == 1234568901L ); BOOST_TEST( *lp == 1234568901L ); ck( static_cast(sp.get()), 1234568901L ); ck( lp, *sp ); sp.reset(); BOOST_TEST( sp.get() == 0 ); // test scoped_ptr with a user defined type boost::scoped_ptr udt_sp ( new UDT( 999888777 ) ); BOOST_TEST( udt_sp->value() == 999888777 ); udt_sp.reset(); udt_sp.reset( new UDT( 111222333 ) ); BOOST_TEST( udt_sp->value() == 111222333 ); udt_sp.reset( new UDT( 333222111 ) ); BOOST_TEST( udt_sp->value() == 333222111 ); // test scoped_array with a build-in type char * sap = new char [ 100 ]; boost::scoped_array sa ( sap ); BOOST_TEST( sa.get() == sap ); BOOST_TEST( sap == sa.get() ); strcpy( sa.get(), "Hot Dog with mustard and relish" ); BOOST_TEST( strcmp( sa.get(), "Hot Dog with mustard and relish" ) == 0 ); BOOST_TEST( strcmp( sap, "Hot Dog with mustard and relish" ) == 0 ); BOOST_TEST( sa[0] == 'H' ); BOOST_TEST( sa[30] == 'h' ); sa[0] = 'N'; sa[4] = 'd'; BOOST_TEST( strcmp( sap, "Not dog with mustard and relish" ) == 0 ); sa.reset(); BOOST_TEST( sa.get() == 0 ); // test shared_ptr with a built-in type int * ip = new int; boost::shared_ptr cp ( ip ); BOOST_TEST( ip == cp.get() ); BOOST_TEST( cp.use_count() == 1 ); *cp = 54321; BOOST_TEST( *cp == 54321 ); BOOST_TEST( *ip == 54321 ); ck( static_cast(cp.get()), 54321 ); ck( static_cast(ip), *cp ); boost::shared_ptr cp2 ( cp ); BOOST_TEST( ip == cp2.get() ); BOOST_TEST( cp.use_count() == 2 ); BOOST_TEST( cp2.use_count() == 2 ); BOOST_TEST( *cp == 54321 ); BOOST_TEST( *cp2 == 54321 ); ck( static_cast(cp2.get()), 54321 ); ck( static_cast(ip), *cp2 ); boost::shared_ptr cp3 ( cp ); BOOST_TEST( cp.use_count() == 3 ); BOOST_TEST( cp2.use_count() == 3 ); BOOST_TEST( cp3.use_count() == 3 ); cp.reset(); BOOST_TEST( cp2.use_count() == 2 ); BOOST_TEST( cp3.use_count() == 2 ); cp.reset( new int ); *cp = 98765; BOOST_TEST( *cp == 98765 ); *cp3 = 87654; BOOST_TEST( *cp3 == 87654 ); BOOST_TEST( *cp2 == 87654 ); cp.swap( cp3 ); BOOST_TEST( *cp == 87654 ); BOOST_TEST( *cp2 == 87654 ); BOOST_TEST( *cp3 == 98765 ); cp.swap( cp3 ); BOOST_TEST( *cp == 98765 ); BOOST_TEST( *cp2 == 87654 ); BOOST_TEST( *cp3 == 87654 ); cp2 = cp2; BOOST_TEST( cp2.use_count() == 2 ); BOOST_TEST( *cp2 == 87654 ); cp = cp2; BOOST_TEST( cp2.use_count() == 3 ); BOOST_TEST( *cp2 == 87654 ); BOOST_TEST( cp.use_count() == 3 ); BOOST_TEST( *cp == 87654 ); #if defined( BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP ) using boost::swap; #endif boost::shared_ptr cp4; int ggg = cp2.use_count(); ggg = cp4.use_count(); swap( cp2, cp4 ); ggg = cp2.use_count(); ggg = cp4.use_count(); BOOST_TEST( cp4.use_count() == 3 ); BOOST_TEST( *cp4 == 87654 ); BOOST_TEST( cp2.get() == 0 ); std::set< boost::shared_ptr > scp; scp.insert(cp4); BOOST_TEST( scp.find(cp4) != scp.end() ); BOOST_TEST( scp.find(cp4) == scp.find( boost::shared_ptr(cp4) ) ); // test shared_array with a built-in type char * cap = new char [ 100 ]; boost::shared_array ca ( cap ); BOOST_TEST( ca.get() == cap ); BOOST_TEST( cap == ca.get() ); BOOST_TEST( &ca[0] == cap ); strcpy( ca.get(), "Hot Dog with mustard and relish" ); BOOST_TEST( strcmp( ca.get(), "Hot Dog with mustard and relish" ) == 0 ); BOOST_TEST( strcmp( cap, "Hot Dog with mustard and relish" ) == 0 ); BOOST_TEST( ca[0] == 'H' ); BOOST_TEST( ca[30] == 'h' ); boost::shared_array ca2 ( ca ); boost::shared_array ca3 ( ca2 ); ca[0] = 'N'; ca[4] = 'd'; BOOST_TEST( strcmp( ca.get(), "Not dog with mustard and relish" ) == 0 ); BOOST_TEST( strcmp( ca2.get(), "Not dog with mustard and relish" ) == 0 ); BOOST_TEST( strcmp( ca3.get(), "Not dog with mustard and relish" ) == 0 ); BOOST_TEST( ca.use_count() == 3 ); BOOST_TEST( ca2.use_count() == 3 ); BOOST_TEST( ca3.use_count() == 3 ); ca2.reset(); BOOST_TEST( ca.use_count() == 2 ); BOOST_TEST( ca3.use_count() == 2 ); BOOST_TEST( ca2.use_count() == 1 ); ca.reset(); BOOST_TEST( ca.get() == 0 ); boost::shared_array ca4; swap( ca3, ca4 ); BOOST_TEST( ca4.use_count() == 1 ); BOOST_TEST( strcmp( ca4.get(), "Not dog with mustard and relish" ) == 0 ); BOOST_TEST( ca3.get() == 0 ); std::set< boost::shared_array > sca; sca.insert(ca4); BOOST_TEST( sca.find(ca4) != sca.end() ); BOOST_TEST( sca.find(ca4) == sca.find( boost::shared_array(ca4) ) ); // test shared_array with user defined type boost::shared_array udta ( new UDT[3] ); udta[0].value( 111 ); udta[1].value( 222 ); udta[2].value( 333 ); boost::shared_array udta2 ( udta ); BOOST_TEST( udta[0].value() == 111 ); BOOST_TEST( udta[1].value() == 222 ); BOOST_TEST( udta[2].value() == 333 ); BOOST_TEST( udta2[0].value() == 111 ); BOOST_TEST( udta2[1].value() == 222 ); BOOST_TEST( udta2[2].value() == 333 ); udta2.reset(); BOOST_TEST( udta2.get() == 0 ); BOOST_TEST( udta.use_count() == 1 ); BOOST_TEST( udta2.use_count() == 1 ); BOOST_TEST( UDT::UDT_use_count == 4 ); // reality check // test shared_ptr with a user defined type UDT * up = new UDT; boost::shared_ptr sup ( up ); BOOST_TEST( up == sup.get() ); BOOST_TEST( sup.use_count() == 1 ); sup->value( 54321 ) ; BOOST_TEST( sup->value() == 54321 ); BOOST_TEST( up->value() == 54321 ); boost::shared_ptr sup2; sup2 = sup; BOOST_TEST( sup2->value() == 54321 ); BOOST_TEST( sup.use_count() == 2 ); BOOST_TEST( sup2.use_count() == 2 ); sup2 = sup2; BOOST_TEST( sup2->value() == 54321 ); BOOST_TEST( sup.use_count() == 2 ); BOOST_TEST( sup2.use_count() == 2 ); // std::cout << "OK\n"; // new char[12345]; // deliberate memory leak to verify leaks detected #undef ck #undef BOOST_TEST #undef boost #undef scoped_ptr #undef shared_ptr #undef scoped_array #undef shared_array } /// Muestra como el compilador no permite la mescla de \c lkptr<> con \c array_lkptr<> . template void test_lkptr::array_vs_lkptr() { lkptr lk; array_lkptr vec; #if 0 vec = lk; // error: no match for 'operator=' in 'vec = lk' lk = vec; // error: no match for 'operator=' in 'lk = vec' lk.merge( vec ); // error: no matching function for call to lkptr::merge(array_lkptr&) vec.merge( lk ); // error: no matching function for call to array_lkptr::merge(lkptr&) lk.reset( vec ); // error: no matching function for call to lkptr::reset(array_lkptr&) vec.reset( lk ); // error: no matching function for call to array_lkptr::reset(lkptr&) #endif } // AutoRef::Auto-Referencia struct AutoRef { static int m_cont; ///< Cantidad total de objetos lkptr m_otro; ///< Enlace circular a otro objeto AutoRef() { m_cont++; } ///< Constructor (aumenta cuenta de objetos) ~AutoRef() { m_cont--; #ifndef AutoRef_BOOOOMMMM m_otro.weak_release(); #endif } ///< Destructor (libera \c lkptr<>) void set( AutoRef * ptr ) { m_otro.reset( ptr ); } ///< Entrelaza a \c ptr. static int cont() { return m_cont; } ///< Contidad total de objetos }; int AutoRef::m_cont = 0; /// Muestra como no es posible que \c lkptr<> maneje algunos ciclos correctamente. template void test_lkptr::test_lkptr_cycle() { if (false) // blows when executed {{ // test::lkptr_cycle() if ( true ) { assertTrue( AutoRef::cont() == 0 ); lkptr paco ( new AutoRef() ); assertTrue( AutoRef::cont() == 1 ); lkptr lola ( new AutoRef() ); assertTrue( AutoRef::cont() == 2 ); paco->set( lola.get() ); lola->set( paco.get() ); } assertTrue( AutoRef::cont() == 0 ); }} } // AutoRef::Auto-Referencia // Esta documentación aparece después para que quede de primera la // implementación a la que se hace referencia en el \dontinclude /** \struct AutoRef \brief Clase que se auto-referencia. \code paco lola +---+ +---+ - Al destruir el "AutoRef" al que "paco" apunta | * | | * | el destructor lkptr<> destruye a lo que "lola" apunta +---+ +---+ | | - Antes de destruir el "AutoRef" al que "lola" apunta v v el destructor lkptr<> destruye a lo que "paco" apunta +-----+ +-----+ | *--|--->| | - Esto causa un ciclo infinito de invocaciones de los destructores | |<---|--* | +-----+ +-----+ - Por eso este programa se cae, pues tiene lkptr<>'s circulares AutoRef AutoRef \endcode \dontinclude test_lkptr.cpp \skipline AutoRef::Auto-Referencia \until AutoRef::Auto-Referencia \see test_lkptr::test_lkptr_cycle() */ /// Number of elements in vector \c V[]. #define DIM(V) (sizeof(V)/sizeof(*V)) #if 0 // test::Point struct Point { virtual int val() const { return 0; } }; struct Circle : public Point { virtual int val() const { return 1; } }; struct Cilinder : public Circle { virtual int val() const { return 2; } }; struct Square : public Point { virtual int val() const { return 3; } }; struct Rectangle : public Square { virtual int val() const { return 4; } }; // test::Rectangle #else #include "figures.h" // (Point (Circle (Cilinder)) (Square (Rectangle))) #endif /// Tests that \c lkptr works well with a class hierarchy. template void test_lkptr::inheritance() { {{ // test::inheritance lkptr VEC[5]; VEC[0].reset( new Point() ); VEC[1].reset( new Circle() ); VEC[2].reset( new Cilinder() ); VEC[3].reset( new Square() ); VEC[4].reset( new Rectangle() ); for (int i=0; ival() == i ); assertTrue( VEC[i].ok() ); } }} } #if 0 struct grandpa { const char * g; grandpa() : g ("grandpa") {} virtual const char* doit() const { return g; } virtual ~grandpa() {} }; struct pa : public grandpa { const char * p; pa() : p("pa") {} virtual const char* doit() const { return p; } }; struct me : public pa { const char * m; me() : m("me") {} virtual const char* doit() const { return m; } }; #else #include "figures.h" // grandpa -> pa -> me #endif /// Tries with 3 pointes to the same value. template void test_lkptr::downcast() { lkptr G ( new grandpa() ); lkptr P ( new pa() ); lkptr ME ( new me() ); lkptr up; lkptr down; assertTrue( G.ok() && P.ok() && ME.ok() && up.ok() && down.ok() ); assertTrue( G->doit() != P->doit() ); assertTrue( P->doit() != ME->doit() ); assertTrue( ME->doit() != G->doit() ); grandpa *pG = G.get(); pa *pP = P.get(); me *pME = ME.get(); assertTrue( 0==dynamic_cast(pG) ); // base ==> derived assertTrue( 0==dynamic_cast(pP) ); assertTrue( 0==dynamic_cast(pG) ); assertTrue( G.ok() && P.ok() && ME.ok() && up.ok() && down.ok() ); assertTrue( 0!=dynamic_cast(pME) ); // derived ==> base assertTrue( 0!=dynamic_cast(pP) ); assertTrue( 0!=dynamic_cast(pME) ); assertTrue( G.ok() && P.ok() && ME.ok() && up.ok() && down.ok() ); down = G; assertTrue( down->doit() == G->doit() ); down = P; assertTrue( down->doit() == P->doit() ); down = ME; assertTrue( down->doit() == ME->doit() ); assertTrue( G.ok() && P.ok() && ME.ok() && up.ok() && down.ok() ); up = G; assertTrue( up.get() == 0 ); up = P; assertTrue( up.get() == 0 ); up = ME; assertTrue( up->doit() == ME->doit() ); up = lkptr(G); assertTrue( G.ok() && P.ok() && ME.ok() && up.ok() && down.ok() ); } #if 0 struct myself { int v; myself() : v(1) { } ~myself() { v=-1; } lkptr eval() { return lkptr(this); } }; #endif /// Tries with 3 pointes to the same value. template void test_lkptr::selfReturn() { lkptr me ( new myself() ); assertTrue( me->v == 1 ); lkptr you; you = me->eval(); assertTrue( me->v == 1 ); assertTrue( you->v == 1 ); me.merge(you); assertTrue( you == me ); assertTrue( me.ok() ); assertTrue( you.ok() ); } /// Returns the pointer within \c ptr. template lkptr test_lkptr::foo( lkptr r ) { assertTrue( r.use_count() == 2 ); assertTrue( *r.b.ptr == 12 ); assertTrue( *r.b.next->ptr == 12 ); assertTrue( r.b.prev != &r.b ); assertTrue( r.b.next != &r.b ); assertTrue( r.b.next->next == &r.b ); assertTrue( r.b.prev->prev == &r.b ); return lkptr( r.get() ); } /// Unrelated lkptr<>'s point to the same object. template void test_lkptr::multiDestroyInt() { test_lkptr::pInt = new int(12); assertTrue( *pInt == 12 ); { lkptr tik = lkptr( pInt ); lkptr tak; assertTrue( *tik == 12 ); tak.reset( foo( tik ) ); // tik && tak point to the same object assertTrue( tik.ok() ); assertTrue( tak.ok() ); assertTrue( tik.use_count() == 1 ); // tik && tak are bound together assertTrue( tak.use_count() == 1 ); if (true) { // use lktpr<>::merge() to avoid multiple detruction tik.merge(tak); assertTrue( tik.ok() ); assertTrue( tak.ok() ); assertTrue( tik.use_count() == 2 ); // tik && tak are bound together assertTrue( tak.use_count() == 2 ); assertTrue( tik == tak ); // both share the same object assertTrue( *tik == *tak ); assertTrue( tik.b.next == &tak.b ); assertTrue( tik.b.prev == &tak.b ); assertTrue( tak.b.next == &tik.b ); assertTrue( tak.b.prev == &tik .b); assertTrue( tak.b.ptr == tik.b.ptr ); assertTrue( tak.b.ptr != 0 ); assertTrue( tik.use_count() == 2 ); // tik && tak are bound together assertTrue( tak.use_count() == 2 ); *tik = 15; assertTrue( *tak.b.ptr == 15 ); *tak = 16; assertTrue( tik.ok() ); assertTrue( tak.ok() ); *tak = 17; // pInt is deleted by tik or tak pInt = 0; } else { // wrong branch assertTrue( tik.use_count() == 1 ); // tik is unrelated to tak assertTrue( tak.use_count() == 1 ); assertTrue( tik == tak ); // both point to the same object assertTrue( *tik == *tak ); // they don´t share the same object // Error: double destruction for tik && tak } if (false) {/* Note: Automatic conversion into a pointer is discouraged because it can lead to serious errors. If the conversion lktpr::operator X*() exists, it would convert any lkptr into its X*, which makes it very easy the following erroneous usage: tik.reset( tak.get() ); // [ERROR] tik.reset( tak.operator X*() ); This will causes double destruction of the pointed object because there are 2 unlinked that own the same object. */ X* ptrX = new X(); lkptr tik ( ptrX ); lkptr tak; tik = tak; assertTrue( tik.use_count() == 2 ); // ok tik.reset( tak.get() ); // [ERROR] tik.reset( tak.operator X*() ); assertTrue( tik.use_count() == 1 ); assertTrue( tak.use_count() == 1 ); // unlinked assertTrue( tik.b.next != &tak.b ); // they are unlinked assertTrue( tak.b.prev != &tik.b ); assertTrue( tik.b.ptr == tak.b.ptr ); // both share the same object } // ptrX gets destroyed twice } { /// http://www.di-mare.com/adolfo/p/lkptr.htm#fg-08 #define assert(X) assertTrue(X) lkptr tik ( new int(12) ); lkptr tak; assert( *tik == 12 ); tak.reset( tik.get() ); // tik && tak apuntan al mismo objeto if (true) { // con lktpr<>::merge() se evita la destrucción múltiple tik.merge(tak); assert( tik.use_count() == 2 ); // tik && tak están atados assert( tak.use_count() == 2 ); assert( tik == tak ); // ambos comparten el mismo objeto assert( *tik == *tak ); } else { assert( tik.use_count() == 1 ); // tik no está relacionado a tak assert( tak.use_count() == 1 ); assert( tik == tak ); // ambos apuntan al mismo objeto assert( *tik == *tak ); // no comparten el mismo objeto // Error: double destrucción de tik && tak } #undef assert } } #if 0 struct selfBase { int v; int del; // # of times deleted selfBase( int n ) : v(n),del(0) { } virtual ~selfBase() { del--; } operator int() const { return v; } }; struct selfDerived : public selfBase { selfDerived( int n ) : selfBase(n) { } virtual ~selfDerived() {} lkptr foo(); }; /// Returns an unrelated pointer to \c this. lkptr selfDerived::foo() { return lkptr(this); } #else #include "figures.h" #endif /// Unrelated lkptr<>'s point to the same object. template void test_lkptr::multiDestroyClass() { lkptr tik = lkptr( new selfDerived(12) ); lkptr tak; assertTrue( *tik == 12 ); tak = tik->foo(); // tik && tak point to the same object if (true) { // use lktpr<>::merge() to avoid multiple detruction tik.merge(tak); assertTrue( tik.use_count() == 2 ); // tik && tak are bound together assertTrue( tak.use_count() == 2 ); assertTrue( tik == tak ); // both share the same object assertTrue( *tik == *tak ); assertTrue( tik.b.next == &tak.b ); assertTrue( tik.b.prev == &tak.b ); assertTrue( tak.b.next == &tik.b ); assertTrue( tak.b.prev == &tik.b ); assertTrue( tik.use_count() == 2 ); // tik && tak are bound together assertTrue( tak.use_count() == 2 ); assertTrue( tak.b.ptr->del == 0 ); assertTrue( tik.b.ptr->del == 0 ); } else { // wrong branch assertTrue( tik.use_count() == 1 ); // tik is unrelated to tak assertTrue( tak.use_count() == 1 ); assertTrue( tik == tak ); // both point to the same object assertTrue( *tik == *tak ); // they don´t share the same object // Error: double destruction for tik && tak } } /// Message for assertTrue( vec[50].use_count() == n ); // msg = msg_vecB(B, vec[B].use_count(), n+1, i, j ); const char* msg_vecB( int B, int u, int n, int i, int j ) { static char buff[128]; char num[3]; #define setNum(m) \ if ((m)<10) { num[0]='0'+(m); num[1]=0; } \ else { num[0]='0'+(m)/10; num[1]='0'+(m)%10; num[2]=0; } buff[0] = 0; strcat( buff, "( " ); setNum(u); strcat( buff, num ); strcat( buff, " == vec[" ); setNum(B); strcat( buff, num ); strcat( buff, "].use_count() != "); setNum(n); strcat( buff, num ); strcat( buff, " ) ==> vec[" ); setNum(B+i); strcat( buff, num ); strcat( buff, "]"); if (j<=n) { strcat( buff, ".merge( vec[" ); setNum(B+j); strcat( buff, num ); strcat( buff, "] )" ); } return buff; #undef setNum } /// Unrelated lkptr<>'s point to the same object. template void test_lkptr::multiDestroyVector() { { assertTrue( pInt == 0 ); pInt = new int(12); assertTrue( pInt != 0 ); } const int SZ = 100; lkptr vec[SZ]; for ( int i=0; i's assertTrue( vec[i] == vec[0] ); assertTrue( vec[i].get() == pInt ); } for ( int i=0; i [ (10 11) (12) (13) ] ==> // [ (10 11 12) (13) ] ==> [ (10 11 12 13) ] vec[10].merge( vec[11] ) ; // 1 ~ 1 assertTrue( vec[10].ok() ); vec[11].merge( vec[12] ) ; // 2 ~ 1 assertTrue( vec[10].use_count() == 3 ); assertTrue( vec[10].use_count() == vec[11].use_count() ); assertTrue( vec[11].use_count() == vec[12].use_count() ); assertTrue( vec[10].ok() ); vec[12].merge( vec[13] ) ; // 3 ~ 1 assertTrue( vec[12].use_count() == 4 ); assertTrue( vec[12].use_count() == vec[13].use_count() ); // [ (20) (21) (22) (23) ] ==> [ (20) (21) (22 23) ] ==> // [ (20) (21 22 23) ] ==> [ (20 21 22 23) ] vec[22].merge( vec[23] ) ; // 1 ~ 1 [ (20) (21) (22 23) ] assertTrue( vec[22].ok() ); vec[22].merge( vec[21] ) ; // 1 ~ 2 [ (20) (21 22 23) ] assertTrue( vec[22].use_count() == 3 ); assertTrue( vec[22].use_count() == vec[23].use_count() ); assertTrue( vec[22].use_count() == vec[21].use_count() ); assertTrue( vec[22].ok() ); vec[20].merge( vec[21] ) ; // 1 ~ 3 [ (20 21 22 23) ] assertTrue( vec[20].use_count() == 4 ); assertTrue( vec[20].use_count() == vec[23].use_count() ); // [ (30) (31) (32) ] vec[30].merge( vec[31] ) ; // 1 ~ 1 [ (30 31) (32) ] vec[31].merge( vec[32] ) ; // 2 ~ 1 [ (30 31 32) ] // [ (33) (34) ] vec[33].merge( vec[34] ) ; // 1 ~ 1 [ (33 34) ] vec[30].merge( vec[33] ) ; // 3 ~ 2 [ (30 31 32 33 34) ] assertTrue( vec[30].use_count() == 5 ); assertTrue( vec[30].use_count() == vec[34].use_count() ); // [ (40) (41) ] vec[40].merge( vec[41] ) ; // 1 ~ 1 [ (40 41) ] // [ (42) (43) (44) ] vec[42].merge( vec[43] ) ; // 1 ~ 1 [ (42 43) (44) ] vec[44].merge( vec[43] ) ; // 1 ~ 2 [ (42 43 44) ] vec[40].merge( vec[43] ) ; // 2 ~ 3 [ (40 41 42 43 44) ] assertTrue( vec[40].use_count() == 5 ); assertTrue( vec[40].use_count() == vec[43].use_count() ); // [ (20 21 22 23) ] [ (40 41 42 43 44) ] assertTrue( vec[20].use_count() == 4 ); assertTrue( vec[40].use_count() == 5 ); vec[20].merge( vec[40] ) ; // 4 ~ 5 [ (20 21 22 23 40 41 42 43 44) ] assertTrue( vec[40].use_count() == 9 ); assertTrue( vec[20].use_count() == vec[40].use_count() ); // [ (30 31 32 33 34) ] [ (10 11 12 13) ] assertTrue( vec[30].use_count() == 5 ); assertTrue( vec[10].use_count() == 4 ); vec[30].merge( vec[10] ) ; // 5 ~ 4 [ (30 31 32 33 34 10 11 12 13) ] assertTrue( vec[30].use_count() == 9 ); assertTrue( vec[10].use_count() == vec[30].use_count() ); vec[0].merge( vec[1] ) ; vec[1].merge( vec[0] ) ; vec[0].merge( vec[0] ) ; vec[1].merge( vec[1] ) ; assertTrue( vec[0].use_count() == 2 ); assertTrue( vec[0].use_count() == vec[1].use_count() ); assertTrue( vec[0].get() == pInt ); assertTrue( vec[0].ok() ); vec[1].merge( vec[2] ) ; // 2 ~ 1 vec[0].merge( vec[1] ) ; // [ (0 1 2) ] vec[1].merge( vec[2] ) ; vec[2].merge( vec[1] ) ; vec[1].merge( vec[0] ) ; assertTrue( vec[0].use_count() == 3 ); assertTrue( vec[0].use_count() == vec[1].use_count() ); assertTrue( vec[1].use_count() == vec[2].use_count() ); assertTrue( vec[0].get() == pInt ); assertTrue( vec[0].ok() ); { assertTrue( 500 ? '0'+i : ' ' ); msg[12] = ( i%10 ); assertTrue_Msg( msg, vec[i].ok() ); } } if (true) { const int LG=8; const int B=50; assertTrue( B+LG0 ? '0'+i : ' ' ); msg[12] = ( i%10 ); assertTrue_Msg( msg, vec[i].ok() ); } } { int max = -1; for ( int i=1; i=0; --i ) { max = ( max hold = vec[33]; assertTrue( hold.use_count() == SZ+1 ); for ( int i=0; i0; --j ) { vec[j] = vec[i]; vec[i] = vec[j]; } } assertTrue( hold.use_count() == SZ+1 ); assertTrue( hold.use_count() != SZ ); assertTrue( hold.get() == pInt && !hold.isNull() ); } pInt = 0; } #include // cout /// Main test program. int main() { test_lkptr tester; // create test tester.run(); // execute std::cout << tester.report(); // Save results on "cout" } // EOF: test_lkptr.cpp