From 2b529e3fac22a0d5da2a4df72c4005ee90b98fb7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 5 Apr 2008 05:57:54 +0200 Subject: [PATCH] customized smart-ptr based on std::shared_ptr hopefully resolves long standing problems with odering and type relations of objects handled via smart ptr --- src/common/p.hpp | 128 +++++++ src/tool/SConscript | 4 +- src/tool/try.cpp | 84 ++++- tests/50components.tests | 20 +- .../components/common/customsharedptrtest.cpp | 315 ++++++++++++++++++ 5 files changed, 541 insertions(+), 10 deletions(-) create mode 100644 src/common/p.hpp create mode 100644 tests/components/common/customsharedptrtest.cpp diff --git a/src/common/p.hpp b/src/common/p.hpp new file mode 100644 index 000000000..d4cc2f11c --- /dev/null +++ b/src/common/p.hpp @@ -0,0 +1,128 @@ +/* + P.hpp - customized shared_ptr with ordering and type relationships + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file p.hpp + ** customized refcounting smart pointer. + ** Template derived from std::tr1::shared_ptr adding total ordering and + ** type relationships implemented by forwarding to the pointees. In all other + ** respects, it should behave exactly as shared_ptr and is able to cooperate + ** and share ownership with other shared_ptr instantiations. + ** + ** By default different instantiations of shared_ptr are completely unrelated types, + ** even if using inherintance related type parameters for instantiation: a shared_ptr + ** isn't some kind-of shared_ptr -- we need to do an explicit static_ptr_cast. Another + ** common problem is the definition of equality and ordering relations for shared_ptr: + ** equality is based on the equality of the managed pointers, while ordering is built upon + ** the ref count. While generally this may be a good compromise, in our case it hinders treating + ** the smart ptrs within the application almost as if they were the objects themselfs and proved + ** an obstacle for writing generic helper functions. + ** + ** the P template resolves these problems by implementing the ordering operators in terms of + ** the corresponding operators on the pointee and by allowing to specify a base class smart-ptr + ** as template parameter. + ** + ** @see asset.hpp + ** @see customsharedptrtest.cpp + ** @see orderingofassetstest.cpp + */ + + +#ifndef LUMIERA_P_H +#define LUMIERA_P_H + + +#include + + +namespace lumiera + { + using std::tr1::shared_ptr; + using std::tr1::weak_ptr; + + /** + * customized refcounting smart pointer template, built upon + * std::tr1::shared_ptr, but forwarding type relationships and + * ordering operators to the pointee objects. + * @param TAR the visible pointee type + * @param the shared-ptr type used as implementation + * @note if the BASE smart-ptr type used as implementation + * implies another pointer type than the one used on + * the interface (=type TAR), then every access to the + * pointee causes an dynamic cast. Thus the pointee types + * need to support RTTI. + */ + template > + class P + : public BASE + { + public: + P ( ) : BASE() {} + template explicit P (Y* p) : BASE(p) {} + template P (Y* p, D d) : BASE(p,d){} + + P (P const& r) : BASE(r) {} + template P (shared_ptr const& r) : BASE(r) {} + template explicit P (weak_ptr const& wr) : BASE(wr) {} + template explicit P (std::auto_ptr & ar) : BASE(ar) {} + + + P& operator= (P const& r) { BASE::operator= (r); return *this; } + template P& operator=(shared_ptr const& sr) { BASE::operator= (sr); return *this; } + template P& operator=(std::auto_ptr & ar) { BASE::operator= (ar); return *this; } + + TAR* get() const { return dynamic_cast (BASE::get()); } + TAR& operator*() const { return *get(); } + TAR* operator->() const { return get(); } + + void swap(P& b) { BASE::swap (b);} + + + private: /* === friend operators injected into enclosing namespace for ADL === */ + template + friend inline bool + operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); } + + template + friend inline bool + operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); } + + template + friend inline bool + operator< (P const& p, P<_O_> const& q) { return (p && q) && (*p < *q); } + + template + friend inline bool + operator> (P const& p, P<_O_> const& q) { return (p && q) && (*q < *p); } + + template + friend inline bool + operator<= (P const& p, P<_O_> const& q) { return (p && q)? (*p <= *q) : (!p && !q); } + + template + friend inline bool + operator>= (P const& p, P<_O_> const& q) { return (p && q)? (*p >= *q) : (!p && !q); } + + }; + + +} // namespace lumiera +#endif diff --git a/src/tool/SConscript b/src/tool/SConscript index 738483498..bf069ebfb 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -3,11 +3,11 @@ ## SConscript - SCons buildscript for tool subdirectory (called by SConstruct) ## -Import('env','artifacts') +Import('env','artifacts','core') # build the ubiquitous Hello World application (note: C source) artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') - + env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature: + + env.Program('#$BINDIR/try', ['try.cpp'] + core) #### to try out some feature: ] diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 349ca7db0..2151f4e84 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -7,18 +7,90 @@ // execute with NOBUG_LOG='ttt:TRACE' bin/try // 1/08 - working out a static initialisation problem for Visitor (Tag creation) // 1/08 - check 64bit longs +// 4/08 - comparison operators on shared_ptr #include #include +#include -#include +#include "proc/asset/media.hpp" using std::string; using std::cout; -NOBUG_CPP_DEFINE_FLAG(test); + +namespace lumiera + { + using std::tr1::shared_ptr; + using std::tr1::weak_ptr; + + template > + class P + : public BASE + { + public: + P() : BASE() {} + template explicit P (Y* p) : BASE(p) {} + template P (Y* p, D d) : BASE(p,d) {} + + P (P const& r) : BASE(r) {} + template P (shared_ptr const& r) : BASE(r) {} + template explicit P (weak_ptr const& wr) : BASE(wr) {} + template explicit P (std::auto_ptr & ar) : BASE(ar) {} + + private: + // friend operators injected into enclosing namespace and found by ADL: + template + friend inline bool + operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); } + + template + friend inline bool + operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); } + + template + friend inline bool + operator< (P const& p, P<_O_> const& q) { return (p && q) && (*p < *q); } + + template + friend inline bool + operator> (P const& p, P<_O_> const& q) { return (p && q) && (*q < *p); } + + template + friend inline bool + operator<= (P const& p, P<_O_> const& q) { return (p && q)? (*p <= *q) : (!p && !q); } + + template + friend inline bool + operator>= (P const& p, P<_O_> const& q) { return (p && q)? (*p >= *q) : (!p && !q); } + + }; +} + +namespace asset + { + using lumiera::P; + + + void + doIt() + { + Asset::Ident key2("Try-1", Category(VIDEO)); + P a1 = asset::Media::create(key2, "Name-1"); + + Asset::Ident key3("Try-2", Category(VIDEO)); + P a2 = asset::Media::create(key3, "Name-2"); + + cout << "a1 = " << str(a1); + + bool yes = (a1 >= a2); + enable_if, string>::type yup; + + cout << "\n yes=" << yes << " yup=" << typeid(*a1).name(); + } +} int main (int argc, char* argv[]) @@ -26,12 +98,10 @@ int main (int argc, char* argv[]) NOBUG_INIT; - int64_t lol (1); - cout << sizeof(lol)<< "\n"; + asset::doIt(); + + cout << "\ngulp\n"; - cout << "long: "<< std::numeric_limits::max() - <<" 64: " << std::numeric_limits::max() - <<"\n"; return 0; } diff --git a/tests/50components.tests b/tests/50components.tests index 0377d08c6..10cf575a5 100644 --- a/tests/50components.tests +++ b/tests/50components.tests @@ -2,7 +2,6 @@ TESTING "Component Test Suite: common and basic components" ./test-components -- - TEST "Hello test" HelloWorld_test 3 < [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ] +out: removed 0 ---> [ 1, 2, 3, 4, 5, 6, 7, 8, 9, ] +out: removed 9 ---> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, ] +out: removed 5 ---> [ 0, 1, 2, 3, 4, 6, 7, 8, 9, ] +out: removed 0 2 4 6 8 ---> [ 1, 3, 5, 7, 9, ] +out: removed 1 3 5 7 9 ---> [ 0, 2, 4, 6, 8, ] +out: removed 0 1 2 3 4 5 6 7 8 9 ---> [ ] +out: removed 0 1 2 3 4 5 6 7 8 ---> [ 9, ] +out: removed 1 2 3 4 5 6 7 8 9 ---> [ 0, ] +out: removed 0 1 2 3 4 6 7 8 9 ---> [ 5, ] +END + + TEST "SanitizedIdentifier_test" SanitizedIdentifier_test < 'Word' out: 'a Sentence' --> 'a_Sentence' diff --git a/tests/components/common/customsharedptrtest.cpp b/tests/components/common/customsharedptrtest.cpp new file mode 100644 index 000000000..7be41b801 --- /dev/null +++ b/tests/components/common/customsharedptrtest.cpp @@ -0,0 +1,315 @@ +/* + CustomSharedPtr(Test) - refcounting, equality and comparisons + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "common/test/run.hpp" +#include "common/util.hpp" + +#include "common/p.hpp" + +#include + +using std::string; + + +namespace asset + { + namespace test + { + using lumiera::P; + using std::tr1::shared_ptr; + using std::tr1::weak_ptr; + + + struct X + : boost::totally_ordered + { + long x_; + + X(long x=0) : x_(x) {} + operator long () { return x_; } + + bool operator< (const X& ox) const { return x_ < ox.x_; } + bool operator== (const X& ox) const { return x_ == ox.x_; } + + virtual ~X() {} // using RTTI + }; + + struct XX : public X + { + long xx_; + + XX(long x=0) : X(x), xx_(x+1) {} + }; + + + /**************************************************************** + * @test assure correct behaviour of lumiera's custom shared-ptr, + * including ADL of operators, shared ownership, typing + * and ordering + * @see lumiera::P + */ + class CustomSharedPtr_test : public Test + { + virtual void + run (Arg arg) + { + check_refcounting (); + check_shared_ownership (); + check_type_relations (); + check_ordering (); + } + + + /** @test smart-ptr basic behaviour */ + void + check_refcounting () + { + P p1 (new X(7)); + ASSERT (p1); + ASSERT (1 == p1.use_count()); + ASSERT (7 == p1->x_); + + { + P p2 (new X(9)); + ASSERT (1 == p2.use_count()); + + p2.swap (p1); + ASSERT (1 == p1.use_count()); + ASSERT (1 == p2.use_count()); + + p2 = p1; + ASSERT (2 == p1.use_count()); + ASSERT (2 == p2.use_count()); + } + + ASSERT (1 == p1.use_count()); + ASSERT (9 == p1->x_); + + p1.reset(); + ASSERT (0 == p1.use_count()); + ASSERT (!p1); + } + + + /** @test cooperation with other shared-ptr types */ + void + check_shared_ownership () + { + std::auto_ptr au (new X(22)); + ASSERT (au.get()); + + P pX (au); + ASSERT (!au.get()); + ASSERT (pX); + ASSERT (1 == pX.use_count()); + ASSERT (22 == pX->x_); + + weak_ptr wX (pX); + ASSERT (wX.lock()); + ASSERT (1 == pX.use_count()); + + shared_ptr sp1 (wX); + shared_ptr sp2 (pX); + shared_ptr sp3; sp3 = pX; + + ASSERT (22 == sp3->x_); + ASSERT (4 == pX.use_count()); + ASSERT (*pX == *sp1); + ASSERT (*sp1 == *sp2); + ASSERT (*sp2 == *sp3); + + P pX2; + pX2.swap(pX); + ASSERT (!pX); + ASSERT (0 == pX.use_count()); + ASSERT (4 == pX2.use_count()); + + P > pXX (pX2); // a different type, but compatible pointers + pX2 = pX; + ASSERT (!pX2); + ASSERT (0 == pX2.use_count()); + ASSERT (4 == pXX.use_count()); + + sp3 = sp2 = sp1 = pX; + ASSERT (22 == pXX->x_); + ASSERT (1 == pXX.use_count()); + ASSERT (!sp1); + ASSERT (!sp2); + ASSERT (!sp3); + + ASSERT (22 == wX.lock()->x_); + ASSERT (1 == pXX.use_count()); + + pXX.reset(); + ASSERT (!pXX); + ASSERT (!wX.lock()); + } + + + /** @test building type relationships on smart-ptrs */ + void + check_type_relations () + { + P pX; // Base: shared_ptr + P pX1; // Base: shared_ptr + P > pX2; // Base: P + P > pX3; // Base: shared_ptr + P > pLo;// Base: shared_ptr + P pLoL; // Base: std::string + P pLoLoL; // Base: shared_ptr + + ASSERT (INSTANCEOF (P, &pX)); + ASSERT (INSTANCEOF (shared_ptr, &pX)); + + ASSERT ( INSTANCEOF (shared_ptr, &pX1)); +// ASSERT (!INSTANCEOF (shared_ptr, &pX1)); // doesn't compile (no RTTI) -- that's OK +// ASSERT (!INSTANCEOF (P, &pX1)); + + ASSERT ( INSTANCEOF (shared_ptr, &pX2)); +// ASSERT (!INSTANCEOF (shared_ptr, &pX2)); + ASSERT ( INSTANCEOF (P, &pX2)); + + ASSERT ( INSTANCEOF (shared_ptr, &pX3)); +// ASSERT (!INSTANCEOF (shared_ptr, &pX3)); +// ASSERT (!INSTANCEOF (P, &pX3)); + + ASSERT ( INSTANCEOF (shared_ptr, &pLo)); +// ASSERT (!INSTANCEOF (shared_ptr, &pLo)); +// ASSERT (!INSTANCEOF (P, &pLo)); + +// ASSERT (!INSTANCEOF (shared_ptr, &pLoL)); +// ASSERT (!INSTANCEOF (shared_ptr, &pLoL)); +// ASSERT (!INSTANCEOF (P, &pLoL)); + ASSERT ( INSTANCEOF (string, &pLoL)); + + ASSERT ( INSTANCEOF (shared_ptr, &pLoLoL)); +// ASSERT (!INSTANCEOF (string, &pLoLoL)); +// ASSERT (!INSTANCEOF (shared_ptr, &pLoLoL)); + + pX = pX1; // OK: pointee subtype... + pX = pX2; // invokes shared_ptr::operator= (shared_ptr const&) + pX = pX3; +// pX = pLo; // similar, but long* not asignable to X* +// pX = pLoL; // similar, but string* not asignable to X* +// pX = pLoLoL; // same... + // you can't do much with the "LoLo"-Types, + // their types and pointee types's relations don't match + + pX.reset (new XX(5)); + ASSERT (5 == *pX); // implicit conversion from X to long + + pX2 = pX; // works, because both are implemented in terms of shared_ptr + ASSERT (5 == pX2->x_); + ASSERT (6 == pX2->xx_); // using the XX interface (performing dynamic downcast) + + pX3.reset (new X(7)); // again works because implemented in terms of shared_ptr + pX2 = pX3; // same + ASSERT (pX2); // both contain indeed a valid pointer.... + ASSERT (pX3); + ASSERT (! pX2.get()); // but dynamic cast to XX at access fails + ASSERT (! pX3.get()); + } + + + /** @test equality and ordering operators forwarded to pointee */ + void + check_ordering () + { + typedef P PX; + typedef P PXX; + + PX pX1 (new X(3)); + PX pX2 (new XX(5)); + PX pX3, pX4, pX5, pX6; + PXX pXX (new XX(7)); + + pX3 = pXX; + pX4.reset(new X(*pXX)); + + ASSERT ( (pX1 == pX1)); // reflexivity + ASSERT (!(pX1 != pX1)); + ASSERT (!(pX1 < pX1)); + ASSERT (!(pX1 > pX1)); + ASSERT ( (pX1 <= pX1)); + ASSERT ( (pX1 >= pX1)); + + ASSERT (!(pX1 == pX2)); // compare to same ptr type with larger pointee of subtype + ASSERT ( (pX1 != pX2)); + ASSERT ( (pX1 < pX2)); + ASSERT (!(pX1 > pX2)); + ASSERT ( (pX1 <= pX2)); + ASSERT (!(pX1 >= pX2)); + + ASSERT (!(pX2 == pXX)); // compare to ptr subtype with larger pointee of same subtype + ASSERT ( (pX2 != pXX)); + ASSERT ( (pX2 < pXX)); + ASSERT (!(pX2 > pXX)); + ASSERT ( (pX2 <= pXX)); + ASSERT (!(pX2 >= pXX)); + + ASSERT (!(pX1 == pXX)); // transitively compare to ptr subtype with larger pointee of subtype + ASSERT ( (pX1 != pXX)); + ASSERT ( (pX1 < pXX)); + ASSERT (!(pX1 > pXX)); + ASSERT ( (pX1 <= pXX)); + ASSERT (!(pX1 >= pXX)); + + ASSERT ( (pX3 == pXX)); // compare ptr to subtype ptr both referring to same pointee + ASSERT (!(pX3 != pXX)); + ASSERT (!(pX3 < pXX)); + ASSERT (!(pX3 > pXX)); + ASSERT ( (pX3 <= pXX)); + ASSERT ( (pX3 >= pXX)); + + ASSERT ( (pX4 == pXX)); // compare ptr to subtype ptr both referring to different but equal pointees + ASSERT (!(pX4 != pXX)); + ASSERT (!(pX4 < pXX)); + ASSERT (!(pX4 > pXX)); + ASSERT ( (pX4 <= pXX)); + ASSERT ( (pX4 >= pXX)); + + ASSERT (!(pXX == pX5)); // compare subtype ptr to empty ptr: "unequal but not orderable" + ASSERT ( (pXX != pX5)); + ASSERT (!(pXX < pX5)); + ASSERT (!(pXX > pX5)); + ASSERT (!(pXX <= pX5)); + ASSERT (!(pXX >= pX5)); + + ASSERT ( (pX5 == pX6)); // compare two empty ptrs: "equal, aequivalent but not orderable" + ASSERT (!(pX5 != pX6)); + ASSERT (!(pX5 < pX6)); + ASSERT (!(pX5 > pX6)); + ASSERT ( (pX5 <= pX6)); + ASSERT ( (pX5 >= pX6)); + } + }; + + + /** Register this test class... */ + LAUNCHER (CustomSharedPtr_test, "unit common"); + + + + } // namespace test + +} // namespace asset