// [xeona project information and licence omitted] // AD-HOC NOTES // // This file represents the stand-alone version of this unit. // It is therefore no longer part of 'xeona' proper. // // Development environment // // boost : 1.39.0 (built from source) // compiler : GCC g++ 4.1.2 (2006) // kernel : Ubuntu 6.10 Linux 2.6.17-12-generic // hardware : 1.4GHz Intel Celeron M / 512MiB RAM // // Build and run calls // // $ g++ -ggdb -Wall -pedantic -Wno-long-long -Weffc++ // frag-assign-ptr-boost-users-1.cc // -o frag-assign-ptr-boost-users-1 // // $ valgrind --leak-check=yes --show-reachable=yes // ./frag-assign-ptr-boost-users-1 // // Design issues // // - get the caller-side C-type information working in 'reVamp' // // Implementation issues and/or bugs // // - the data members in 'xeona::applied_ptr<>' should be private but are public // - resolve the 'remove_if' "problem" // LOCAL AND SYSTEM INCLUDES #include // STL copying, searching, and sorting #include // STL function objects #include // setw() and family #include // standard io #include // string-streams #include // standard exception classes, runtime_error() #include // C++ strings #include // run-time type information (RTTI), std::bad_cast exception #include // STL sequence container #include // safe type-heterogeneous storage #include // BOOST_FOREACH iteration macro #include // printf style formatting // PREPROCESSOR MACROS FOR TEST PURPOSES // verbosity control // 0 = fully silent, 1 = warn messages, 2 = internal, 3 = + ctors, 4 = + dtors namespace { const unsigned verbose = 4; } // FOR USE OUTSIDE 'XEONA' #include // C-style char classification, case conversion #include // Boost shared pointer (serializable) #include // Boost weak pointer (serializable) #include // Boost smart pointer support using boost::shared_ptr; using boost::weak_ptr; using boost::enable_shared_from_this; using boost::const_pointer_cast; using boost::static_pointer_cast; using boost::dynamic_pointer_cast; using boost::bad_weak_ptr; // exception class namespace xeona { std::string demangle // somewhat crude name demangler :) (std::string tid) { #ifdef __GNUC__ // CAUTION: g++ compiler specific code block if ( ! tid.empty() ) // protection needed while ( std::isdigit(tid.at(0)) ) // see tid.erase(0, 1); // remove first char #endif // __GNUC__ return tid; } } // namespace 'xeona' // CODE // --------------------------------------------------------- // notes : xeona::assign_ptr overview // --------------------------------------------------------- // Description : "remappable" counted pointer which partly mimics 'shared_ptr' // Role : primarily developed for the 'xeona' lazy link process // Techniques : pointer semantics, "revamp" function, 'shared_ptr' // Status : first-pass complete // // Design notes // // This reference counted pointer class has the ability to // switch the controlled resource via the 'revamp' function // -- a feature which is intrinsically IMPOSSIBLE with // 'shared_ptr', as described here: // // Boost Libraries // + Smart Pointers // + shared_ptr class template // + Smart Pointer Programming Techniques // + Obtaining a shared_ptr from a raw pointer // // Having said that, this class template relies on // 'shared_ptr's to do most of its work! // // The idea of self-implemented counted pointers came from // Dattatri (2002 pp487-496). // // The interface and usage is modeled on 'shared_ptr' // although not all its free and member functions are // offered here. Read the code for details. // // No attempt has been made to make this code run fast. // Rather the emphasis has been on design cleanliness and // maintainability. // // It is recommended practice not to deploy smart pointers // as unnamed temporaries. // // See also: // '/usr/local/include/boost-1_39/boost/smart_ptr/shared_ptr.hpp' // // Potential extensions // // Add a 'subsume' function, perhaps by applying // 'std::set_union' to 'd_clients': // // void subsume(assign_ptr inferiorPool) // // Note that 'swap' and some other functions from // 'shared_ptr' are not implemented, neither are some of the // associated free functions. // // Neither is any support offered for interoperation with // 'weak_ptr' and 'auto_ptr'. // // The use of custom 'shared_ptr' deleters, passed thru to // the underlying resource, could also be considered. // // CAUTION: avoid language-defined types // // This class should not be used on language-defined types // such as 'int' because '->' produces strange semantics // (Dattatri 2002 p496). // // CAUTION: heap objects only // // Stack objects should not be deployed and will yield // run-time faults on attempted deletion. // // CAUTION: 'shared_ptr' construction is non-exclusive // // The use of pre-existing 'shared_ptr' objects in // construction and also in 'revamp' and 'reset' calls means // that the resource might not be exclusive. This // eventuality can be tested using the member function // 'exclusive'. // // CAUTION: possibly thread-UNSAFE as implemented // // Consult Dattatri for ideas as to how one might confirm or // make this class thread-safe. // // Background // // Alexandrescu (2001, ch7, pp157-195) devotes a chapter to // smart pointers and the underlying design trade-offs -- // however "revamping" is not considered: // // The notion of "revamping" is never far away in Meyers // (1996 item29 pp183-213) reference counting example, but // neither is the concept directly mentioned. // // Some ideas regarding the use of the Boost.Any library // came from Karlsson (2006 ch6 pp159-189). // // Finally, Dattatri (2002 pp487-496) provides an // implementation of counted pointers and discusses the // design issues in some depth. // // References // // Alexandrescu, Andrei. 2001. Modern C++ design : generic // programming and design patterns applied. Addison-Wesley, // Boston, USA. ISBN 0-201-70431-5. // // Dattatri, Kayshav. 2002. C++ : effective-object // oriented software construction : concepts, principles, // industrial strategies and practices -- Second edition. // Prentice Hall PTR, Upper Saddle River, New Jersey, USA. // ISBN 0-13-086769-1. // // Karlsson, Bjoern. 2006. Beyond the C++ Standard Library // : an introduction to Boost. Addison-Wesley, Upper Saddle // River, New Jersey, USA. ISBN 0-321-13354-4. // // Meyers, Scott. 1996. More effective C++ : 35 new ways // to improve your programs and design. Addison-Wesley, // Boston, USA. ISBN 0-201-63371-X. // // --------------------------------------------------------- // --------------------------------------------------------- // CLASS : ::Deport // --------------------------------------------------------- // Description : reporting class // Role : debug reporting // Techniques : file scope, Boost.Format library, '::verbose' // Status : complete // // Overall verbosity control via local constant '::verbose' // // 0 = fully silent // 1 = warn (right-side) messages // 2 = all internal messages // 3 = above plus constructor reports // 4 = above plus destructor reports // // --------------------------------------------------------- namespace { class Deport { // LOCAL ENUMERATIONS private: enum Verbose { silent = 0, report = 1 }; // DISABLED private: Deport(); // zero-argument constructor Deport(const Deport& orig); // copy constructor Deport& operator= (const Deport& orig); // copy assignment operator public: // CREATORS explicit Deport (const std::string& host, // class name const std::string& call, // member function name const Verbose verbose = report, std::ostream& ostream = std::cout) : // default ostream set here d_ostream(ostream), d_flag("** assign pointer : "), // margin flag string set here d_lead(boost::str(boost::format("%s %12s : %-28s") % d_flag % host % call)), d_verbose(verbose) { // assumes that the object instantiation call was placed at // the beginning of the function block if ( ::verbose > 2 ) operator()("enter", "", d_verbose); } ~Deport() { if ( ::verbose > 3 ) operator()("exit", "", d_verbose); } // MEMBER FUNCTIONS void operator() (const std::string& msg1, // left column message const std::string& msg2 = "", // right column message const Verbose verbose = report) // can set to 'Deport::silent' { if ( ::verbose == 0 ) return; // '::verbose' defined in file if ( ::verbose == 1 && msg2.empty() ) return; if ( verbose == silent) return; // 'verbose' is an argument if ( msg1.empty() && msg2.empty() ) return; d_ostream << d_lead << " : "; if ( msg2.empty() ) d_ostream << msg1 << std::endl; else d_ostream << boost::format("%-30s %38s **") % msg1 % msg2 << std::endl; } // INSTANCE DATA private: std::ostream& d_ostream; const std::string d_flag; const std::string d_lead; Verbose d_verbose; // private enum }; } // unnamed namespace namespace xeona { // --------------------------------------------------------- // CLASS : xeona::bad_assign_ptr // --------------------------------------------------------- // Description : C++ exception class // Role : (none thus far) // Techniques : C++ exception mechanism // Status : complete // --------------------------------------------------------- class bad_assign_ptr : public std::exception { public: bad_assign_ptr() : d_message() { } bad_assign_ptr (const std::string message) : d_message(message) { } ~bad_assign_ptr() throw() // no-throw { } virtual char const* what() const throw() // no-throw { std::string msg = "xeona::bad_assign_ptr"; if ( ! d_message.empty() ) msg += ": " + d_message; return msg.c_str(); } private: const std::string d_message; }; // --------------------------------------------------------- // CLASS : xeona::bad_assign_cast // --------------------------------------------------------- // Description : C++ exception class // Role : as required // Techniques : C++ exception mechanism // Status : complete // --------------------------------------------------------- class bad_assign_cast : public std::exception { public: bad_assign_cast() : d_message() { } bad_assign_cast (const std::string message) : d_message(message) { } ~bad_assign_cast() throw() // no-throw { } virtual char const* what() const throw() // no-throw { std::string msg = "xeona::bad_assign_cast"; if ( ! d_message.empty() ) msg += ": " + d_message; return msg.c_str(); } private: const std::string d_message; }; // --------------------------------------------------------- // CLASS : xeona::const_cast_tag // CLASS : xeona::dynamic_cast_tag // CLASS : xeona::polymorphic_cast_tag // --------------------------------------------------------- // Description : built into overloaded cast constructors // Role : used by the associated free function // Techniques : 'struct' // Status : complete // --------------------------------------------------------- struct const_cast_tag {}; //struct static_cast_tag {}; // not currently implemented struct dynamic_cast_tag {}; struct polymorphic_cast_tag {}; // --------------------------------------------------------- // CLASS : xeona::Client (abstract base class) // --------------------------------------------------------- // Description : interface class for class 'ClientImp<>' // Role : support client registration // Techniques : inheritance, pure virtual functions // Status : complete -- but see comments elsewhere about polymorphic behavior // --------------------------------------------------------- class Client { // DISABLED private: Client& operator= (const Client& orig); // copy assignment operator public: // CREATORS virtual ~Client() { Deport deport("Client", "destructor"); } Client() { Deport deport("Client", "constructor"); } Client(const Client& orig) // copy constructor, required by 'clone' { } // ACCESSORS virtual const std::type_info& type() const = 0; // MANIPULATORS virtual Client* // derived class template is 'ClientImp*' get() = 0; virtual bool update (shared_ptr replacement) = 0; // CAUTION: smart 'void*' generic pointer virtual bool updateAny (const boost::any& replacement) = 0; // CAUTION: payload will need to carry a 'C' }; // --------------------------------------------------------- // CLASS : xeona::ClientImp <> // --------------------------------------------------------- template class assign_ptr; // forward (partial) declaration template class ClientImp : public Client { // DISABLED private: ClientImp(); // zero-argument constructor ClientImp& operator= (const ClientImp& orig); // copy assignment operator public: ~ClientImp() { Deport deport("ClientImp", "destructor"); } explicit ClientImp (assign_ptr* client) : Client(), d_client(client) { Deport deport("ClientImp", "constructor"); std::ostringstream oss; oss << "C = " << xeona::demangle(typeid(**client).name()); deport(oss.str()); } ClientImp(const ClientImp& orig) : // copy constructor, required by 'clone' d_client(orig.d_client) // shallow copy is correct { } // COMPARATORS // logical equality function friend bool operator== (const ClientImp& a, const ClientImp& b) { Deport deport("free func", "ClientImp operator=="); return a.d_client == b.d_client; } // ACCESSORS virtual const std::type_info& type() const { return typeid(C); } // MANIPULATORS // "If the function's return type is a pointer (or a // reference) to a base class, the derived class's function // may return a pointer (or reference) to a class derived // from that base class." (Meyers 1996 pp126-127). virtual ClientImp* get() { return this; } virtual bool update (shared_ptr replacement) { // WARNING: the generic pointer signature here defeats the // strong type system! And in particular, the following // is not caught because 'static_pointer_cast' and not // 'dynamic_pointer_cast' (see below) must be used: // // assign_ptr next(new Next()); // next.revamp(new Base()); // // Shared_ptr type 'void' is deployed because the // following declaration will not compile: // // template // virtual // bool update(shared_ptr globalReplacement) // reporting Deport deport("ClientImp", __func__); // key call d_client->d_resource = static_pointer_cast(replacement); // check result and return accordingly if ( d_client->d_resource == 0 ) { deport("WARN", "BAD STATIC_POINTER_CAST"); return false; // return failure } else { return true; // return success } } virtual bool updateAny (const boost::any& replacement) { Deport deport("ClientImp", __func__); // CAUTION: The Boost.Any cast specifier must be exact -- // which means the 'replacement' payload must carry a 'C' // and nothing else. // CAUTION: the definition of 'buffer' in an 'if' test is // legal and remains visible in both the 'if' and 'else' // blocks (Alexandrescu 2001 p238). if ( const shared_ptr* buffer = boost::any_cast >(&replacement) ) { d_client->d_resource = *buffer; // guaranteed type match on good cast deport("good boost::any_cast"); return true; } else { deport("bad boost::any_cast"); return false; } } template bool updateNonVirtual (shared_ptr replacement) { // reporting Deport deport("ClientImp", __func__); // key call d_client->d_resource = replacement; // return success return true; } // INSTANCE DATA private: assign_ptr* d_client; }; // --------------------------------------------------------- // CLASS : xeona::Common // --------------------------------------------------------- // Description : helper class, holds client list, manages the revamp process // Role : held by 'assign_ptr' as an aggregate data member // Techniques : 'std::vector', casting // Status : complete -- but could refactor the 'std::remove_if' predicate // // Design notes // // CAUTION: 'std::remove_if' design flaws // // The member function 'detach' MAY NEED more work related // to the 'std::remove_if' predicate -- in particular read // Karlsson (2006 pp186-188), Josuttis (1999 pp302-304, // 378-379), and Lischner (2003 p355). Note that Lischner // mistakenly states that 'remove_if' acts on 'false'. // // Indeed, the functor class template 'match' should // either be wrapped by or reimplemented as a predicate // function. Moreover, the predicate function/functor // could also meet the STL library adapter requirements. // // This is only a problem for 'std::remove_if'. // // --------------------------------------------------------- template class assign_ptr; // forward (partial) declaration template class match; // forward (partial) declaration class Common { // DISABLED private: Common(const Common& orig); // copy constructor Common& operator= (const Common& orig); // copy assignment operator public: // CREATORS ~Common() // destructor { Deport deport("Common", "destructor"); } Common() : // zero-argument constructor d_clients(), // empty vector d_revampCount(0) { Deport deport("Common", "constructor"); } // GENERAL PURPOSE FUNCTIONS // --------------------------------------------------------- // MEMBER FUNCTION : xeona::Common::attach <> // --------------------------------------------------------- // register me template void attach(assign_ptr* client) { Deport deport("Common", __func__); shared_ptr temp(new ClientImp(client)); d_clients.push_back(temp); std::ostringstream oss; oss << "client count = " << d_clients.size() << ", " << "revamp count = " << d_revampCount; deport(oss.str()); } // --------------------------------------------------------- // MEMBER FUNCTION : xeona::Common::detach <> // --------------------------------------------------------- // unregister me template void detach(assign_ptr* client) { Deport deport("Common", __func__); shared_ptr > temp(new ClientImp(client)); // delete (remove and erase) ALL matches -- whilst noting // that 'erase' is a 'std::vector' member function and // 'std::remove_if' is from const unsigned before = d_clients.size(); // [1] d_clients.erase(std::remove_if(d_clients.begin(), d_clients.end(), match(temp)), // [2] d_clients.end()); // [1] size type: strictly speaking, member function 'size' // returns type 'std::vector // >::size_type' -- which is 'unsigned' on my system // // [2] CAUTION: 'std::remove_if' predicate: see notes // elsewhere! const int diff = before - d_clients.size(); if ( diff != 1 ) { deport("WARN", "DIFF NOT ONE"); std::ostringstream ess; ess << "'xeona::assign_ptr::reset' 'Common::detach'" << " coding error, 'diff' not one but " << diff; throw std::logic_error(ess.str()); } std::ostringstream oss; oss << "diff = " << diff; deport(oss.str()); } // --------------------------------------------------------- // MEMBER FUNCTION : xeona::Common::reVamp <> // --------------------------------------------------------- // // CAUTION: incomplete design // // This code does not implement resource convertibility // in a way that covers all cases and is type-checked -- // in fact, achieving both would appear to be a major // challenge. // // Check the pre-processor macros in this code to see // which conversions are currently supported. // // Type-convertibility requirements // // type T - results from the caller of 'revamp' // type C - relates to the CURRENT client under traversal // type Y - is from the replacement object // // type-convertibility (where "convertible" implies "is // or derives from" -- in other words, only direct // assignment or implicit upcasting are acceptable): // // Y convertible to C convertible to T // // T and Y are known directly from this call under // compile-time polymorphism // // C is known indirectly thru run-time polymorphism and // the 'Client' class hierarchy, namely: // // class Client // template class ClientImp : public Client // // Alexandrescu (2001 p264) gives an overview of // polymorphism in C++. // // --------------------------------------------------------- template // 'T' is nominated in call, 'Y' is implicit bool reVamp(shared_ptr globalReplacement) { Deport deport("Common", __func__); // administration d_revampCount++; bool retval = true; // assume true initially // set client count std::ostringstream oss1; oss1 << "client count = " << d_clients.size(); const std::string clients = oss1.str();; // initial reporting deport(clients); // loop the clients int loopCount = 0; BOOST_FOREACH( shared_ptr client, d_clients ) { // update and set loop count ++loopCount; std::ostringstream oss; oss << "count = " << std::setw(2) << std::setfill(' ') << loopCount; const std::string loops = oss.str(); // reporting deport("loop enter, " + loops); // integrity check if ( client == 0 ) { deport("WARN", "EMPTY OR NULL CLIENT"); continue; } #if 0 // proxy for 'xeona' proper, in the absence of anything else // --------------------------------- // 'xeona' code // --------------------------------- // CAUTION: special code for 'xeona' in which 'client' // must be exactly of type 'Y' for an update to occur // -- this code uses Boost.Any -- see r3280 for earlier // code based on typeid comparison and 'void*' const boost::any payload = globalReplacement; // naturally Y const bool retY = client->updateAny(payload); // [1] key call // [1] this call uses "boost::any >" as // its cast specifier, which means that C and Y must // match for a successful outcome. if ( retY ) { // only one such update per pool should occur retval = true; // one or more true means success deport("client and resource type match", "UPDATE"); } else { // CAUTION: at one stage, 'client->d_resource' was // set to empty pointer "shared_ptr()" but this // created a bad bug whereby the 'xeona' 'polylink' // caller object was disabled and then destroyed // before the call had completed and returned. deport("client and resource type differ - skipping"); } #else // 0, in other words, normal code // --------------------------------- // normal code // --------------------------------- std::ostringstream oss8; oss8 << "payload = " << xeona::demangle(client->type().name()); deport(oss8.str()); # if 0 // 0 = type-unsafe code, 1 = process only Y- and T-payloads // try Y payload const boost::any payloadY = globalReplacement; if ( client->updateAny(payloadY) == false ) { // try T payload const boost::any payloadT = dynamic_pointer_cast(globalReplacement); if ( client->updateAny(payloadT) == false ) { retval = false; // one or more false means failure } } # else if ( client->update(globalReplacement) == false ) // key call { retval = false; // one or false false means failure } # endif // 0 // BUGGY: direct calls not working as expected // // client->get()->updateNonVirtual(globalReplacement); // client->clone()->updateNonVirtual(globalReplacement); // // note: error: 'class xeona::Client' has // no member named 'updateNonVirtual' // --------------------------------- #endif // 0 deport("loop leave"); } // BOOST_FOREACH std::ostringstream oss2; oss2 << "revamp count = " << d_revampCount; const std::string revamps = oss2.str(); deport(clients + ", " + revamps); // return statement return retval; } long clientCount() const { Deport deport("Common", __func__); return d_clients.size(); } long revampCount() const { Deport deport("Common", __func__); return d_revampCount; } // INSTANCE DATA private: std::vector > d_clients; // actually 'ClientImp' long d_revampCount; // initially zero }; // --------------------------------------------------------- // CLASS (FUNCTOR) : xeona::match <> // --------------------------------------------------------- template class match { // DISABLED private: match(); // zero-argument constructor match& operator= (const match& orig); // copy assignment operator // CREATORS public: match (const shared_ptr >& target) : d_target(target), d_match(0), d_count(new int(0)) { Deport deport("match", "constructor", Deport::silent); deport("in constructor"); } match(const match& orig) : // copy constructor d_target(orig.d_target), // shallow copy is correct d_match(orig.d_match), d_count(orig.d_count) // shallow copy is correct { } ~match() // destructor { Deport deport("match", "destructor", Deport::silent); deport(boost::str(boost::format("matches = %d, count = %d") % d_match % *d_count)); } // FUNCTION CALL OPERATOR bool operator() (const shared_ptr& current) { Deport deport("match", "function call operator", Deport::silent); std::stringstream oss; oss << "client = " << current << ", " << "target = " << d_target; deport(oss.str()); shared_ptr > downcast = dynamic_pointer_cast >(current); if ( downcast == 0 ) { deport("Client downcast failed"); return false; } const bool match = ( *downcast == *d_target ); // object-wise comparison if ( match == true ) { deport("match found"); ++d_match; ++*d_count; } return match; } // CAUTION: [1] according to Josuttis (1999 p378) 'remove_if' // acts on 'true' but Lischner (2003 p355) says 'remove_if' // acts on 'false' -- however Josuttis is correct. // INSTANCE DATA private: const shared_ptr > d_target; int d_match; shared_ptr d_count; }; // --------------------------------------------------------- // CLASS : xeona::assign_ptr <> // --------------------------------------------------------- // Description : "remappable" counted pointer which partly mimics 'shared_ptr // Role : point of access // Techniques : 'shared_ptr' // Status : complete -- but see preliminary notes for potential extensions // // Template arguments // // 'T' is invoked class // 'Y' is argument class, which needs to be a sub-class of 'T' // 'U' is argument class, which needs to be a super-class of 'T' // // --------------------------------------------------------- template class assign_ptr { // FRIENDS friend class Common; // used by Common::reVamp public: // CREATORS // UPCASTABLE: the various Y-to-T creators (more precisely, // function templates) require that the underlying resource // be naturally convertible from 'shared_ptr' to // 'shared_ptr'. virtual ~assign_ptr(); // destructor explicit assign_ptr(); // zero-argument (hollow) constructor template explicit assign_ptr(Y* resource); // upclass raw pointer constructor // NON-EXCLUSIVITY: the provision of an overloaded // 'shared_ptr' constructor together with the equivalent // 'revamp' and 'reset' member functions require the client to // acknowledge that non-exclusive use may result -- in other // words, not all instances of the controlled resource are // under the jurisdiction of the current 'assign_ptr' pool. template // can be NON-EXCLUSIVE explicit assign_ptr(shared_ptr resource); // upclass shared pointer constructor assign_ptr(const assign_ptr& orig); // normal copy constructor template assign_ptr(const assign_ptr& orig); // upclass copy constructor // COPY ASSIGNMENT OPERATORS assign_ptr& operator= (const assign_ptr& orig); // copy assignment operator template assign_ptr& operator= (const assign_ptr& orig); // upclass copy assignment oper // POINTER SEMANTICS operator bool() const; // object status test T& operator* () const; // dereference operator T* operator-> () const // member access operator throw (std::logic_error); // exception specification // COMPARATORS -- FREE FUNCTIONS // logical equality function template friend bool operator== (const assign_ptr& a, const assign_ptr& b); // logical NOT equality function template friend bool operator!= (const assign_ptr& a, const assign_ptr& b); // POOL-WIDE FUNCTIONS template bool revamp(Y* resource); // replace resource for entire pool template // can be NON-EXCLUSIVE bool revamp(shared_ptr resource); // replace resource for entire pool long client_count() const; // pool count bool unique() const; // returns 'true' if sole client long revamp_count() const; // revamp count bool original() const; // returns 'true' if never revamped long external_count() const; // number of "uncontrolled" resources bool exclusive() const; // 'true' is no "uncontrolled" resource // INDIVIDUAL-ACTION FUNCTIONS template void reset(); // split off from existing pool template void reset(Y* resource); // split off from existing pool template // can be NON-EXCLUSIVE void reset(shared_ptr resource); // split off from existing pool // DOWNCAST CONSTRUCTORS - NORMALLY INTERFACED BY FREE FUNCTIONS template assign_ptr(assign_ptr const & pointer, // 'U' is the "cast to" type xeona::const_cast_tag); template assign_ptr(assign_ptr const & pointer, // 'U' is the "cast to" type xeona::dynamic_cast_tag); // returns zero on cast failure template assign_ptr(assign_ptr const & pointer, // 'U' is the "cast to" type xeona::polymorphic_cast_tag) throw(std::bad_cast); // throws on cast failure // REPORT FUNCTION std::string // with trailing newline report(const std::string& identifier = "(not supplied)", const std::string& comment = "", const unsigned leftIndent = 2) const; // CODE DEVELOPMENT CALLS T* get() const; // null not tested, unlike 'operator->' assign_ptr* get_assign(); // const // simply returns 'this' void trash() { revamp(static_cast(0)); } // DANGEROUS // INSTANCE DATA public: // [1] shared_ptr d_resource; // locally held resource shared_ptr d_common; // [1] CAUTION: data access: the upclass copy constructor // requires that these be public -- when they really should // be private (I remain puzzled!). }; // --------------------------------- // implementation // --------------------------------- // DESTRUCTOR // destructor // no action required due to use of 'shared_ptr's template assign_ptr::~assign_ptr() { Deport deport("assign_ptr", "destructor"); } // CONSTRUCTORS // zero-argument (hollow) constructor // makes a new 'Common' in the process template assign_ptr::assign_ptr() : d_resource(), d_common(new Common()) { Deport deport("assign_ptr", "constructor - hollow"); deport("using new Common()"); } // upcast raw pointer constructor template template assign_ptr::assign_ptr(Y* resource) : d_resource(shared_ptr(resource)), d_common(new Common()) { Deport deport("assign_ptr", "constructor - raw upcast"); d_common->attach(this); } // upcast shared pointer constructor template template assign_ptr::assign_ptr(shared_ptr resource) : d_resource(resource), d_common(new Common()) { Deport deport("assign_ptr", "constructor - shared upcast"); d_common->attach(this); } // normal copy constructor template assign_ptr::assign_ptr(const assign_ptr& orig) : d_resource(orig.d_resource), d_common(orig.d_common) { Deport deport("assign_ptr", "copy constr - no cast"); d_common->attach(this); } // upcast copy constructor (requires public data members) template template assign_ptr::assign_ptr(const assign_ptr& orig) : d_resource(orig.d_resource), d_common(orig.d_common) { Deport deport("assign_ptr", "copy constr - upcast"); d_common->attach(this); } // COPY ASSIGNMENT OPERATORS // copy assignment operator template assign_ptr& assign_ptr::operator= (const assign_ptr& orig) { Deport deport("assign_ptr", "copy assign - no cast"); this->d_resource = orig.d_resource; this->d_common = orig.d_common; d_common->attach(this); return *this; } // upcast copy assignment operator template template assign_ptr& assign_ptr::operator= (const assign_ptr& orig) { Deport deport("assign_ptr", "copy assign - upcast"); if ( this != &orig ) // protect against self-assignment { this->d_resource = orig.d_resource; this->d_common = orig.d_common; d_common->attach(this); } return *this; } // OVERLOADED OPERATORS // bool operator // // based on 'std::tr1::shared_ptr' code, see also Becker (2007 // pp35-36) for a discussion on this same operator for // 'shared_ptr' --this streams as 1/0 or true/false, if used // as follows: // // std::cout << myAssignPtr << std::endl; // template assign_ptr::operator bool() const { return d_resource == 0 ? 0 : d_resource.get(); } // dereference operator * template T& assign_ptr::operator* () const { return *d_resource; } // member access operator -> template T* assign_ptr::operator-> () const throw (std::logic_error) // exception specification { if ( d_resource.get() == 0 ) { const std::string emsg = "'xeona::assign_ptr::operator->' call on null resource"; throw std::logic_error(emsg); } return d_resource.get(); } // COMPARATORS -- FREE FUNCTIONS // friends injected into enclosing namespace and found by // argument dependent (name) lookup (ADL) // logical equality function template bool operator== (const assign_ptr& a, const assign_ptr& b) { return a.get() == b.get(); } // logical NOT equality function template bool operator!= (const assign_ptr& a, const assign_ptr& b) { return !(a == b); } // POOL-WIDE FUNCTIONS // raw revamp // wrapper to shared pointer version template template bool assign_ptr::revamp (Y* resource) { Deport deport("assign_ptr", "revamp - raw"); shared_ptr namedTemporary(resource); return revamp(namedTemporary); } // shared revamp template template bool assign_ptr::revamp (shared_ptr resource) { Deport deport("assign_ptr", "revamp - shared"); std::ostringstream oss; oss << "T = " << xeona::demangle(typeid(T).name()) << " " << "Y = " << xeona::demangle(typeid(Y).name()); deport(oss.str()); const bool ret = d_common->reVamp(resource); return ret; } // client count template long assign_ptr::client_count() const { return d_common->clientCount(); } // unique test template bool assign_ptr::unique() const { return ( client_count() == 1 ); } // revamp count template long assign_ptr::revamp_count() const { return d_common->revampCount(); } // original test template bool assign_ptr::original() const { return ( revamp_count() == 0 ); } // external count template long assign_ptr::external_count() const { return d_resource.use_count() - client_count(); } // exclusivity test template bool assign_ptr::exclusive() const { return ( external_count() == 0 ); } // INDIVIDUAL-ACTION FUNCTIONS // empty reset // wrapper to shared pointer version template template void assign_ptr::reset() { Deport deport("assign_ptr", "reset - empty"); reset(shared_ptr()); // empty shared pointer } // raw reset // wrapper to shared pointer version template template void assign_ptr::reset (Y* resource) { Deport deport("assign_ptr", "reset - raw"); shared_ptr namedTemporary(resource); reset(namedTemporary); } // shared reset template template void assign_ptr::reset(shared_ptr resource) { Deport deport("assign_ptr", "reset - shared"); // CAUTION: note the explicit 'T' template argument below d_common->detach(this); d_common = shared_ptr(new Common()); d_common->attach(this); d_resource = resource; } // DOWNCAST CONSTRUCTORS - NORMALLY INTERFACED BY FREE FUNCTIONS template template assign_ptr::assign_ptr // a kind of copy constructor (assign_ptr const & pointer, // 'Y' is the "cast to" type xeona::dynamic_cast_tag) : // tag 'struct' defined earlier d_resource(dynamic_pointer_cast(pointer.d_resource)), d_common(pointer.d_common) { Deport deport("assign_ptr", "dynamic cast ctor"); if ( d_resource == 0 ) // the cast failed { deport("WARN", "CAST FAILED"); d_common = shared_ptr(new Common()); } d_common->attach(this); } template template assign_ptr::assign_ptr // a kind of copy constructor (assign_ptr const & pointer, // 'Y' is the "cast to" type xeona::const_cast_tag) : // tag 'struct' defined earlier d_resource(const_pointer_cast(pointer.d_resource)), d_common(pointer.d_common) { Deport deport("assign_ptr", "const cast ctor"); // it is believed that this cast cannot fail d_common->attach(this); } template template assign_ptr::assign_ptr // a kind of copy constructor (assign_ptr const & pointer, // 'Y' is the "cast to" type xeona::polymorphic_cast_tag) // tag 'struct' defined earlier throw(std::bad_cast): d_resource(dynamic_pointer_cast(pointer.d_resource)), d_common(pointer.d_common) { Deport deport("assign_ptr", "polymorphic cast ctor"); if ( d_resource == 0 ) // the cast failed { std::ostringstream oss; oss << "'xeona::assign_ptr' 'dynamic_pointer_cast' failed" << ", resource = " << pointer.d_resource.get(); throw xeona::bad_assign_cast(oss.str()); d_common = shared_ptr(new Common()); } d_common->attach(this); } // REPORT FUNCTION // report details template std::string assign_ptr::report (const std::string& identifier, // note default const std::string& comment, // note default const unsigned leftIndent) const // note default { std::string rsctype = "(empty shared_ptror null resource)"; if ( d_resource ) rsctype = xeona::demangle(typeid(*d_resource).name()); const std::string fill(leftIndent, ' '); std::ostringstream oss; oss << fill << "assign_ptr details :"; if ( !comment.empty() ) oss << " " << comment << "\n"; else oss << "\n"; oss << fill << " identifier : " << identifier << "\n" << fill << " resource : " << d_resource.get() << "\n" << fill << " ptr type : " << xeona::demangle(typeid(T).name()) << "\n" << fill << " rsc type : " << rsctype << "\n" << fill << " revamps : " << revamp_count() << "\n" << fill << " clients : " << client_count() << "\n" << fill << " externals : " << external_count() << "\n"; return oss.str(); } // CODE DEVELOPMENT CALLS // get // no null protection, unlike 'operator->' template T* assign_ptr::get() const { return d_resource.get(); } // get assign_ptr template assign_ptr* assign_ptr::get_assign() { return this; } // DOWNCAST CONSTRUCTORS // --------------------------------------------------------- // FREE FUNCTION : xeona::const_assign_cast <> // --------------------------------------------------------- // Description : wrapper to modified copy constructor // Role : support for downcast functionality // Techniques : tag struct, 'const_cast' // Status : complete // --------------------------------------------------------- template assign_ptr const_assign_cast (assign_ptr const & pointer) { Deport deport("free func", __func__); return assign_ptr(pointer, xeona::const_cast_tag()); } // --------------------------------------------------------- // FREE FUNCTION : xeona::dynamic_assign_cast <> // --------------------------------------------------------- // Description : wrapper to modified copy constructor // Role : support for downcast functionality // Techniques : tag struct, 'dynamic_cast', return zero on cast fail // Status : complete // --------------------------------------------------------- template assign_ptr dynamic_assign_cast (assign_ptr const & pointer) { Deport deport("free func", __func__); return assign_ptr(pointer, xeona::dynamic_cast_tag()); } // --------------------------------------------------------- // FREE FUNCTION : xeona::polymorphic_assign_cast <> // --------------------------------------------------------- // Description : wrapper to modified copy constructor // Role : support for downcast functionality // Techniques : tag struct, 'dynamic_cast', throw on cast fail // Status : complete // // Design notes // // Unlike dynamic_assign_cast, the function throws a // 'std::bad_cast' on failure. // // See Karlsson (2006 pp54-61) for a discussion of // polymorphic casting. // // --------------------------------------------------------- template assign_ptr polymorphic_assign_cast (assign_ptr const & pointer) throw(std::bad_cast) { Deport deport("free func", __func__); return assign_ptr(pointer, xeona::polymorphic_cast_tag()); } } // namespace 'xeona' // --------------------------------------------------------- // CLASS : Base // CLASS : Next // CLASS : Last // --------------------------------------------------------- class Base { public: Base(const int id) : d_id(id) { } virtual ~Base() { } virtual std::string say() { return "base"; } virtual int getIdentifier() { return d_id; } protected: int d_id; }; class Next : public Base { public: Next(const int id) : Base(id) { } virtual int getIdentifier() { return d_id; } virtual std::string say() { return "next"; } }; class Last : public Next { public: Last(const int id) : Next(id) { } virtual int getIdentifier() { return d_id; } virtual std::string say() { return "last"; } }; // --------------------------------------------------------- // MAIN FUNCTION : main // --------------------------------------------------------- int main(int argc, char* argv[]) { std::string buffer = "hello xeona::assign_ptr<>"; std::cout << buffer << std::endl; using xeona::assign_ptr; std::cout << "TEST 1" << std::endl; { assign_ptr r1(new Next(1)); std::cout << " original (1) = " << r1->getIdentifier() << std::endl; assign_ptr r2(r1); std::cout << " clients (2) = " << r2.client_count() << std::endl; // revamp call r1.revamp(new Next(7)); std::cout << " update (7) = " << r2->getIdentifier() << std::endl; assign_ptr r3 = r2; std::cout << " next (7) = " << r3->getIdentifier() << std::endl; assign_ptr r4 = r2; std::cout << " clients (4) = " << r4.client_count() << std::endl; std::cout << std::boolalpha; std::cout << " unique (no) = " << r4.unique() << std::endl; std::cout << r4.report("r4"); // reset call r4.reset(new Next(9)); std::cout << r4.report("r4"); } std::cout << "TEST 2" << std::endl; { assign_ptr next(new Last(1)); assign_ptr base = next; std::cout << base.report("base", "original"); std::cout << next.report("next", "original"); base.revamp(new Last(2)); next.revamp(new Last(3)); std::cout << base.report("base", "revamp"); std::cout << next.report("next", "revamp"); base.revamp(new Base(4)); // [1] std::cout << base.report("base", "faulty"); std::cout << next.report("next", "faulty"); std::cout << " base, faulty (4) = " << base->getIdentifier() << std::endl; std::cout << " next, faulty (4) = " << next->getIdentifier() << std::endl; } return 0; } // $Source: /home/robbie/synk/xeona/fragments/RCS/frag-assign-ptr-boosters-1-short.cc,v $ // end of file