|
From: stephan b. <sg...@us...> - 2004-12-23 20:12:06
|
Update of /cvsroot/pclasses/pclasses2/include/pclasses In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26210 Modified Files: Factory.h Phoenix.h Log Message: converted spaces to tabs Index: Factory.h =================================================================== RCS file: /cvsroot/pclasses/pclasses2/include/pclasses/Factory.h,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- Factory.h 23 Dec 2004 14:13:35 -0000 1.3 +++ Factory.h 23 Dec 2004 20:11:49 -0000 1.4 @@ -1,5 +1,5 @@ -#ifndef p_BASICFACTORY_H_INCLUDED -#define p_BASICFACTORY_H_INCLUDED 1 +#ifndef p_FACTORY_H_INCLUDED +#define p_FACTORY_H_INCLUDED 1 // Author: stephan beal <st...@s1...> // License: Public Domain @@ -9,242 +9,250 @@ #include <functional> #include <pclasses/Phoenix.h> // i don't like this dep, but i also don't like - // what happens in some cases when i don't use - // phoenix. :/ +// what happens in some cases when i don't use +// phoenix. :/ namespace P { + + /** + Namespace Sharing holds internal "sharing context" marker + classes. + */ + namespace Sharing + { + /** Internal marker class. */ + struct FactoryContext {}; + } + /** + The Hook namespace holds classes intended to be used to + allow client-side code to hook in to the framework's + behaviour, replacing certain parts of the core with their + own. + */ + namespace Hook + { + /** + FactoryCreateHook is a helper object factory for the P::Factory + API. - /** - Namespace Sharing holds internal "sharing context" marker - classes. - */ - namespace Sharing - { - /** Internal marker class. */ - struct FactoryContext {}; - } + General conventions: - /** - The Hook namespace holds classes intended to be used to - allow client-side code to hook in to the framework's - behaviour, replacing certain parts of the core with their - own. - */ - namespace Hook - { - /** - FactoryCreateHook is a helper object factory for the P::Factory - API. + SubT must derive from (or be) T and must be Default + Constructuable on the heap. In short, the following must be + able to succeed: + + <pre> + T * foo = new SubT; + </pre> - General conventions: + Clients may freely specialize this type to hook their + factories in to P, and the above requirement may not be + imposed by client-side specializations. + */ - SubT must derive from (or be) T and must be Default - Constructuable on the heap. In short, the following must be - able to succeed: - - <pre> - T * foo = new SubT; - </pre> + template < class T, class SubT > + struct FactoryCreateHook + { + /** + The type returned by create() and + operator(). + */ + typedef T * result_type; - Clients may freely specialize this type to hook their - factories in to P, and the above requirement may not be - imposed by client-side specializations. - */ + /** + A typedef for the second template parameter for this + type. + */ + typedef SubT actual_type; + + /** + This creates a new SubT, which is assumed to be a + subclass of T. It can be used as a factory for + Factory & class_loader. + + If T or SubT are abstract types, you must + specialize this type such that create() returns 0 + for those. That is, we "simulate" creation of + abstract types by returning 0. - template < class T, class SubT > - struct FactoryCreateHook - { - /** - The type returned by create() and - operator(). - */ - typedef T * result_type; + The caller owns the returned pointer, which + may be 0. + */ + static result_type create() + { + return new actual_type; + } - /** - A typedef for the second template parameter for this - type. - */ - typedef SubT actual_type; - - /** - This creates a new SubT, which is assumed to be a - subclass of T. It can be used as a factory for - Factory & class_loader. - - If T or SubT are abstract types, you must - specialize this type such that create() returns 0 - for those. That is, we "simulate" creation of - abstract types by returning 0. + /** + A factory which always returns 0. This can + be used as a factory when, e.g., T is + abstract. + */ + static result_type no_create() + { + return 0; + } + + /** + Same as create(). + */ + result_type operator()() const + { + return create(); + } + }; - The caller owns the returned pointer, which - may be 0. - */ - static result_type create() - { - return new actual_type; - } - - /** - Same as create(). - */ - result_type operator()() const - { - return create(); - } - }; + /** + FactoryInstanceHook provides a way for non-core + code to manipulate or swap out the object returned + by Factory::instance(). - /** - FactoryInstanceHook provides a way for non-core - code to manipulate or swap out the object returned - by Factory::instance(). + Client code may want to specialize this to, e.g., plug in a + DLL-aware object lookups, auto-loading of plugins, etc. - Client code may want to specialize this to, e.g., plug in a - DLL-aware object lookups, auto-loading of plugins, etc. + Specializing this type on a specific T will cause: - Specializing this type on a specific T will cause: + MyFacT & fac = MyFacT::instance(); - MyFacT & fac = MyFacT::instance(); + to trigger a call to operator()( fac ) the first + time fac is initialized AND on any re-initializations + (which may get called post-main()). - to trigger a call to operator()( fac ) the first - time fac is initialized AND on any re-initializations - (which may get called post-main()). - */ - template <typename FactoryT> - struct FactoryInstanceHook - { - typedef FactoryInstanceHook<FactoryT> ThisType; - - /** - Specializations of this type may initialize - instance() here. It will be called whenever Phoenix - initializes the object. See Phoenix for details. + FactoryT is expected to be of type ::P::Factory<>, + but there is nothing in the implementation which is actually + specific to that type. + */ + template <typename FactoryT> + struct FactoryInstanceHook + { + typedef FactoryInstanceHook<FactoryT> ThisType; + + /** + Specializations of this type may initialize + instance() here. It will be called whenever Phoenix + initializes the object. See Phoenix for details. - The default implementation does nothing. - */ - void operator()( FactoryT & ) throw() - { - } - - /** - The default implementation returns a shared Factory object. - THIS type's operator() will be called on the factory immediately - after creating the factory. - */ - static FactoryT & instance() - { - typedef ::P::Phoenix<FactoryT, ::P::Sharing::FactoryContext, ThisType > PHX; - return PHX::instance(); - } - }; - } // namespace Hook + The default implementation does nothing. + */ + void operator()( FactoryT & ) throw() + { + } + + /** + The default implementation returns a shared Factory object. + THIS type's operator() will be called on the factory immediately + after creating the factory. + */ + static FactoryT & instance() + { + typedef ::P::Phoenix<FactoryT, ::P::Sharing::FactoryContext, ThisType > PHX; + return PHX::instance(); + } + }; + } // namespace Hook - /** - Factory is essentially a static classloader, capable of - loading classes by using registered factories for a given - set of keys (e.g., class names). + /** + Factory is essentially a static classloader, capable of + loading classes by using registered factories for a given + set of keys (e.g., class names). - Classloaders, at least in my experience, need to be able to - load all classes which derive from some given type. Without - a common base class, one can't safely attempt to cast from - an arbitrary pointer to the type we want to load. That's - where the InterfaceT parameter comes in. All objects - instantiated via this loader must inherit from InterfaceT. + Classloaders, at least in my experience, need to be able to + load all classes which derive from some given type. Without + a common base class, one can't safely attempt to cast from + an arbitrary pointer to the type we want to load. That's + where the InterfaceT parameter comes in. All objects + instantiated via this loader must inherit from InterfaceT. - KeyType is a type which specifies the type of key used to - look up classes, defaulting to std::string. + KeyType is a type which specifies the type of key used to + look up classes, defaulting to std::string. - Both InterfaceT and KeyType must be Default Constructable, - and InterfaceT must be constructable on the heap - (e.g., via new InterfaceT()). + Both InterfaceT and KeyType must be Default Constructable, + and InterfaceT must be constructable on the heap + (e.g., via new InterfaceT()). - The default ipmlementation holds no per-instance state, - thus it can be copied quickly. + The default ipmlementation holds no per-instance state, + thus it can be copied quickly. - Sample usage: -<pre> + Sample usage: + + <pre> Factory<MyClass> & fac = Factory<MyClass>::instance(); -fac.registerFactory( "my_key", MyClass::new_instance ); +fac.registerFactory( "my_key", P::Hook::FactoryCreateHook>MyClass<::create ); MyClass *foo = fac.instantiate( "some_key" ); // == NULL foo = fac.instantiate( "my_key" ); // == a new MyClass object -</pre> + </pre> - Note that all instantiators of the same type use the same - object factories. The ContextType template parameter can be - used to limit the scope of the object factory registrations - to a specific context: instantiators with different Contexts - use different maps. ContextType is only used as a type, and - is never instantiated by this class. - */ + Note that all instantiators of the same type use the same + object factories. The ContextType template parameter can be + used to limit the scope of the object factory registrations + to a specific context: Factories with different Contexts + use different maps. ContextType is only used as a marker type, + and is never instantiated by this class. + */ template < class InterfaceT, - class KeyType = std::string, - class ContextType = Sharing::FactoryContext - > - class Factory + class KeyType = std::string, + class ContextType = Sharing::FactoryContext + > + class Factory { - public: + public: - /** - A typedef for the InterfaceT used by this class. - */ + /** + A typedef for the InterfaceT used by this class. + */ typedef InterfaceT value_type; + /** + Same as (InterfaceT *), for conformance with the + Adaptable Unary Functor model. + */ + typedef InterfaceT * result_type; - /** - A typedef for the KeyType used by this class. - */ - typedef KeyType key_type; - - /** Same as ContextType */ - typedef ContextType context_type; - /** - Same as KeyType, for conformance with the the - Adaptable Unary Functor model. - */ - typedef KeyType key_type; + /** + A typedef for the KeyType used by this class. + */ + typedef KeyType key_type; - /** - Same as (InterfaceT *), for conformance with the - Adaptable Unary Functor model. - */ - typedef InterfaceT * result_type; + /** Same as ContextType */ + typedef ContextType context_type; - /** - Convenience typedef. - */ + /** + Convenience typedef. + */ typedef Factory< InterfaceT, KeyType, ContextType > ThisType; - Factory() {} + Factory() {} - virtual ~Factory() {} + virtual ~Factory() {} - /** - The type of factories used by this class: a - function taking void and returning (value_type - *). See factoryMap(). + /** + The type of factories used by this class: a + function taking void and returning (value_type + *). See factoryMap(). - todo: implement proper functor support. - */ - typedef result_type ( *factory_type ) (); + todo: implement proper functor support. + */ + typedef result_type ( *factory_type ) (); - /** - Internal container type used for mapping keys to - factories. - */ + /** + Internal container type used for mapping keys to + factories. + */ typedef std::map < key_type, factory_type > FactoryMap; - /** - Tries to instantiate an instance of value_type - using the given key. Returns NULL if no class could - be loaded for the given key. + /** + Tries to instantiate an instance of value_type + using the given key. Returns NULL if no object + could be loaded for the given key. - Subtypes are free to implement, e.g., DLL lookups. - */ + Subtypes are free to implement, e.g., DLL lookups. + */ virtual result_type instantiate( const key_type & key ) { typename FactoryMap::const_iterator it = factoryMap().find( key ); @@ -256,143 +264,143 @@ } - /** - Returns this->internalInstantiate(key). - */ - result_type operator()( const key_type & key ) - { - return this->internal( key ); - } + /** + Returns this->instantiate(key). + */ + result_type operator()( const key_type & key ) + { + return this->internal( key ); + } /** - Registers a factory using the given key. If fp is - NULL then a default factory is used. Note that fp - may not return a type other than - ThisType::value_type *, but the actual object it - creates may be a polymorphic subclass of - value_type. See the FactoryCreateHook class for a - factory which does this subtype-to-base conversion. - */ + Registers a factory using the given key. If fp is + NULL then a default factory is used. Note that fp + may not return a type other than + ThisType::value_type *, but the actual object it + creates may be a polymorphic subclass of + value_type. See the FactoryCreateHook class for a + factory which does this subtype-to-base conversion. + */ void registerFactory( const key_type & key, factory_type fp ) { - factoryMap().insert( FactoryMap::value_type( key, fp ) ); - } + factoryMap().insert( FactoryMap::value_type( key, fp ) ); + } - /** - Returns the internal key-to-factory map. It is safe - for clients to modify this except in multi-threaded - environments, and then all guarantees go out the - window. That said, it should never be necessary for - clients to use this. + /** + Returns the internal key-to-factory map. It is safe + for clients to modify this except in multi-threaded + environments, and then all guarantees go out the + window. That said, it should never be necessary for + clients to use this. - It is safe to call this post-main(), but such calls - may return an empty map! - */ + It is safe to call this post-main(), but such calls + may return an empty map! + */ static FactoryMap & factoryMap() { return Phoenix<FactoryMap,ThisType>::instance(); } - /** - Returns true if the given key is registered. This is sometimes useful - for checking whether a factory needs to be re-registered, which - is sometimes necessary post-main(), when the internal map gets hosed - before clients are done using it. - */ - bool isRegistered( const key_type & key ) const - { - return factoryMap().end() != factoryMap().find( key ); - } + /** + Returns true if the given key is registered. This is sometimes useful + for checking whether a factory needs to be re-registered, which + is sometimes necessary post-main(), when the internal map gets hosed + before clients are done using it. + */ + bool isRegistered( const key_type & key ) const + { + return factoryMap().end() != factoryMap().find( key ); + } - /** - Returns a shared reference to a Factory. - - Client code may plug in a new default instance() by - specializing Hook::FactoryInstanceHook< FactoryT >. See - that type for details. - */ - static Factory & instance() - { - return Hook::FactoryInstanceHook<ThisType>::instance(); - } + /** + Returns a shared reference to a Factory. + + Client code may plug in a new default instance() by + specializing Hook::FactoryInstanceHook< FactoryT >. See + that type for details. + */ + static Factory & instance() + { + return Hook::FactoryInstanceHook<ThisType>::instance(); + } - + }; // class Factory - /** - NamedTypeFactory works for string-keyed types. It is - expected that this will be the most-used factory - implementation, as non-string-keyed factories are rare in - practice (but sometimes very useful). - */ - template <typename InterfaceT> - struct NamedTypeFactory : public Factory< InterfaceT, std::string, Sharing::FactoryContext > - { - virtual ~NamedTypeFactory(){} - }; + /** + NamedTypeFactory works for string-keyed types. It is + expected that this will be the most-used factory + implementation, as non-string-keyed factories are rare in + practice (but sometimes very useful). + */ + template <typename InterfaceT> + struct NamedTypeFactory : public Factory< InterfaceT, std::string, Sharing::FactoryContext > + { + virtual ~NamedTypeFactory(){} + }; - /** - The CL namespace encapsulates P's classloader-related API. + /** + The CL namespace encapsulates P's classloader-related API. - All of the functions in this API use the obbject - NamedTypeFactory<InterfaceT>::instance() for - factory-related operations. Thus, using the various Hook - classes you can force these functions to use your factory. + All of the functions in this API use the obbject + NamedTypeFactory<InterfaceT>::instance() for + factory-related operations. Thus, using the various Hook + classes you can force these functions to use your factory. - i don't like this namespace. Maybe move these functions - into Factory itself??? They're simply proxying that type, - after all. + i don't like this namespace. Maybe move these functions + into Factory itself??? They're simply proxying that type, + after all. - */ - namespace CL { - - using namespace ::P; - using namespace ::P::Sharing; + */ + namespace CL { + + using namespace ::P; + using namespace ::P::Sharing; - /** - Registers classname with InterfaceT. If factory_function is 0 - then ::P::Hook::FactoryCreateHook<InterfaceT,InterfaceT>::create is - used. If InterfaceT is abstract then you must specialize - ::P::Hook::FactoryCreateHook<InterfaceT,InterfaceT>, as documented - in FactoryCreateHook::create(). - */ - template <typename InterfaceT> - void registerBase( const std::string & classname, InterfaceT *(*factory_function)() = 0 ) - { - NamedTypeFactory<InterfaceT>::instance().registerFactory( classname, - ( 0 != factory_function ) - ? factory_function - : ::P::Hook::FactoryCreateHook<InterfaceT,InterfaceT>::create - ); - } - /** - Registers a factory creating ImplT objects with the - InterfaceT classloader. If factory_function is 0 then - ::P::Hook::FactoryCreateHook<InterfaceT,ImplT>::create is - used. - */ - template <typename InterfaceT, typename ImplT> - void registerSubtype( const std::string & classname, InterfaceT *(*factory_function)() = 0 ) - { - NamedTypeFactory<InterfaceT>::instance().registerFactory( classname, - ( 0 != factory_function ) - ? factory_function - : ::P::Hook::FactoryCreateHook<InterfaceT,ImplT>::create - ); - } - - /** - Returns the same as NamedTypeFactory<InterfaceT>::instance().instantiate( classname ). - */ - template <typename InterfaceT> - InterfaceT * classload( const std::string & classname ) - { - return NamedTypeFactory<InterfaceT>::instance().instantiate( classname ); - } - } // namespace CL + /** + Registers classname with InterfaceT. If factory_function is 0 + then ::P::Hook::FactoryCreateHook<InterfaceT,InterfaceT>::create is + used. If InterfaceT is abstract then you must specialize + ::P::Hook::FactoryCreateHook<InterfaceT,InterfaceT>, as documented + in FactoryCreateHook::create(). + */ + template <typename InterfaceT> + void registerBase( const std::string & classname, InterfaceT *(*factory_function)() = 0 ) + { + NamedTypeFactory<InterfaceT>::instance().registerFactory( classname, + ( 0 != factory_function ) + ? factory_function + : ::P::Hook::FactoryCreateHook<InterfaceT,InterfaceT>::create + ); + } + /** + Registers a factory creating ImplT objects with the + InterfaceT classloader. If factory_function is 0 then + ::P::Hook::FactoryCreateHook<InterfaceT,ImplT>::create is + used. + */ + template <typename InterfaceT, typename ImplT> + void registerSubtype( const std::string & classname, InterfaceT *(*factory_function)() = 0 ) + { + NamedTypeFactory<InterfaceT>::instance().registerFactory( classname, + ( 0 != factory_function ) + ? factory_function + : ::P::Hook::FactoryCreateHook<InterfaceT,ImplT>::create + ); + } + + /** + Returns the same as NamedTypeFactory<InterfaceT>::instance().instantiate( classname ). + */ + template <typename InterfaceT> + InterfaceT * classload( const std::string & classname ) + { + return NamedTypeFactory<InterfaceT>::instance().instantiate( classname ); + } + } // namespace CL } // namespace P -#endif // p_BASICFACTORY_H_INCLUDED +#endif // p_FACTORY_H_INCLUDED Index: Phoenix.h =================================================================== RCS file: /cvsroot/pclasses/pclasses2/include/pclasses/Phoenix.h,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- Phoenix.h 23 Dec 2004 04:45:52 -0000 1.1 +++ Phoenix.h 23 Dec 2004 20:11:49 -0000 1.2 @@ -10,7 +10,7 @@ //////////////////////////////////////////////////////////////////////////////// #include <stdlib.h> // atexit() -#include <iostream> // cout/cerr +#include <iostream> // cout/cerr, only for debuggering #ifndef phoenix_DEBUG @@ -21,8 +21,8 @@ #if phoenix_DEBUG # include <typeinfo> # define phoenix_CERR std::cerr << __FILE__ << ":" << std::dec << __LINE__ << " : " \ - << "Phoenix<"<<typeid((base_type *)NULL).name()<<" , " \ - << typeid((context_type *)NULL).name()<<"> " + << "Phoenix<"<<typeid((base_type *)NULL).name()<<" , " \ + << typeid((context_type *)NULL).name()<<"> " #else # define phoenix_CERR if(0) std::cerr #endif // phoenix_DEBUG @@ -34,179 +34,179 @@ */ namespace P { - - /** - Internal helper class to provide a default no-op - initializer for phoenixed objects. + + /** + Internal helper class to provide a default no-op + initializer for phoenixed objects. - See the Phoenix<> class. - */ - struct no_op_phoenix_initializer - { - /** Does nothing: This class is called no_op for a reason ;) */ - template <typename T> - void operator()( T & ) throw() { return; } - }; + See the Phoenix<> class. + */ + struct no_op_phoenix_initializer + { + /** Does nothing: This class is called no_op for a reason ;) */ + template <typename T> + void operator()( T & ) throw() { return; } + }; - /** - Phoenix is class for holding singleton-style instances of - BaseType objects. Rather than requiring that BaseType be a - Singleton type, Phoenix subclasses BaseType to add the - Phoenix-like capabilities. Phoenixing makes the shared - object post-main() safe, in terms of object destruction - order. + /** + Phoenix is class for holding singleton-style instances of + BaseType objects. Rather than requiring that BaseType be a + Singleton type, Phoenix subclasses BaseType to add the + Phoenix-like capabilities. Phoenixing makes the shared + object post-main() safe, in terms of object destruction + order. - Parameterized on: + Parameterized on: - - BaseType: must be struct or class type and must be - default-constructable. i have no clue what is supposed to - happen if BaseType's dtor is not virtual. That said, - Phoenix has been successfully demonstrated with a BaseType - of std::map, which has no virtual dtor. + - BaseType: must be struct or class type and must be + default-constructable. i have no clue what is supposed to + happen if BaseType's dtor is not virtual. That said, + Phoenix has been successfully demonstrated with a BaseType + of std::map, which has no virtual dtor. - - ContextType: These objects are only singletons within the - given ContextType. That is, Phoenix<T,X>::instance() - will return a different object than Phoenix<T,Y> - will. + - ContextType: These objects are only singletons within the + given ContextType. That is, Phoenix<T,X>::instance() + will return a different object than Phoenix<T,Y> + will. - - InitializerType: must be a unary functor accepting a - BaseType &. It's return value is ignored. The default - functor does nothing. The InitializerType is called when a - to-be-phoenixed object is initially created and whenever it - is phoenixed. This is intended to be used, e.g., for - re-populating a phoenixed shared object. - TODO: investigate the implications of a predicate - initializer, which would return false if the object could - not be initialized. InitializerType::operator() must - not throw. + - InitializerType: must be a unary functor accepting a + BaseType &. It's return value is ignored. The default + functor does nothing. The InitializerType is called when a + to-be-phoenixed object is initially created and whenever it + is phoenixed. This is intended to be used, e.g., for + re-populating a phoenixed shared object. + TODO: investigate the implications of a predicate + initializer, which would return false if the object could + not be initialized. InitializerType::operator() must + not throw. - Whether or not BaseType is technically a singleton depends - on entirely BaseType itself. This class is more often used - to provide easy access to context-dependent shared objects, - rather than pure singletons. The Phoenix class itself is a - true Singleton, but each combination of template arguments - provides a different Singleton *type*, so the end effect is - "context singletons." + Whether or not BaseType is technically a singleton depends + on entirely BaseType itself. This class is more often used + to provide easy access to context-dependent shared objects, + rather than pure singletons. The Phoenix class itself is a + true Singleton, but each combination of template arguments + provides a different Singleton *type*, so the end effect is + "context singletons." - This is another attempt to solve the classic - Keyboard-Console-Log problem, as discussed at length in - <i>Modern C++ Design</i>. It relies on sane behaviour in - the C library's atexit() function, which, as is shown in - MC++D, is not the case on all systems. That said, the - Phoenix-specific behaviours are undefined on those systems, - which is only to say that it might not be post-main() safe. + This is another attempt to solve the classic + Keyboard-Console-Log problem, as discussed at length in + <i>Modern C++ Design</i>. It relies on sane behaviour in + the C library's atexit() function, which, as is shown in + MC++D, is not the case on all systems. That said, the + Phoenix-specific behaviours are undefined on those systems, + which is only to say that it might not be post-main() safe. - Caveats: + Caveats: - i am not 100% clear on all of the implications of this - implementation's approach... my gut tells me i'm missing - some significant bits. i mean, it <i>can't</i> have been - this straightforward to solve ;). The very nature of the - Phoenix Singleton problem makes it difficult to reliably - test in real-world applications. That said, i have seen a - objects be successfully phoenixed and atexit()ed, so it is - known to at least "basically" work. + i am not 100% clear on all of the implications of this + implementation's approach... my gut tells me i'm missing + some significant bits. i mean, it <i>can't</i> have been + this straightforward to solve ;). The very nature of the + Phoenix Singleton problem makes it difficult to reliably + test in real-world applications. That said, i have seen a + objects be successfully phoenixed and atexit()ed, so it is + known to at least "basically" work. - There's a paper about "context singletons", this class, - and some of it's implications, at: + There's a paper about "context singletons", this class, + and some of it's implications, at: - http://s11n.net/misccode/context_singletons.html + http://s11n.net/misccode/context_singletons.html - [Much later: i've gotten more re-use out of this class than - probably any other single class i've ever written.] - */ - template < - typename BaseType, - typename ContextType = BaseType, - typename InitializerType = no_op_phoenix_initializer - > - struct Phoenix : public BaseType - { - /** - context_type is unused by this class, but might be useful - for type identification at some point. - */ - typedef ContextType context_type; - /** - The BaseType parameterized type. - */ - typedef BaseType base_type; + [Much later: i've gotten more re-use out of this class than + probably any other single class i've ever written.] + */ + template < + typename BaseType, + typename ContextType = BaseType, + typename InitializerType = no_op_phoenix_initializer + > + struct Phoenix : public BaseType + { + /** + context_type is unused by this class, but might be useful + for type identification at some point. + */ + typedef ContextType context_type; + /** + The BaseType parameterized type. + */ + typedef BaseType base_type; - /** - The functor type used to initialize this phoenixed object. - */ - typedef InitializerType initializer_type; + /** + The functor type used to initialize this phoenixed object. + */ + typedef InitializerType initializer_type; - /** - Returns a shared instance of this object. + /** + Returns a shared instance of this object. - The instance() method will always return the same - address, though it is potentially possible - (post-main()) that the actual object living at that - address is different from previous calls. + The instance() method will always return the same + address, though it is potentially possible + (post-main()) that the actual object living at that + address is different from previous calls. - It is never a good idea to hold on to the returned - reference for the life of an object, as that bypasses - the phoenixing capabilities. + It is never a good idea to hold on to the returned + reference for the life of an object, as that bypasses + the phoenixing capabilities. - If you ever delete it you're on you're own. That's - a Bad Idea. - */ - static base_type & instance() - { - static this_type meyers; - static bool donethat = false; - if( this_type::m_destroyed ) - { - phoenix_CERR << "Phoenixing!" << std::endl; - donethat = false; - new( &meyers ) this_type; - atexit( this_type::do_atexit ); - } - if( !donethat ) - { - phoenix_CERR << "initializing instance" << std::endl; - donethat = true; - initializer_type()( meyers ); - } - phoenix_CERR << "instance() == " <<std::hex<<&meyers<<std::endl; - return meyers; - } + If you ever delete it you're on you're own. That's + a Bad Idea. + */ + static base_type & instance() + { + static this_type meyers; + static bool donethat = false; + if( this_type::m_destroyed ) + { + phoenix_CERR << "Phoenixing!" << std::endl; + donethat = false; + new( &meyers ) this_type; + atexit( this_type::do_atexit ); + } + if( !donethat ) + { + phoenix_CERR << "initializing instance" << std::endl; + donethat = true; + initializer_type()( meyers ); + } + phoenix_CERR << "instance() == " <<std::hex<<&meyers<<std::endl; + return meyers; + } - private: + private: - /** A convenience typedef. */ - typedef Phoenix<base_type,context_type,initializer_type> this_type; + /** A convenience typedef. */ + typedef Phoenix<base_type,context_type,initializer_type> this_type; - static bool m_destroyed; + static bool m_destroyed; - Phoenix() - { - phoenix_CERR << "Phoenix() @" << std::hex<< this << std::endl; - m_destroyed = false; - } + Phoenix() + { + phoenix_CERR << "Phoenix() @" << std::hex<< this << std::endl; + m_destroyed = false; + } - virtual ~Phoenix() - { - phoenix_CERR << "~Phoenix() @" << std::hex<< this << std::endl; - m_destroyed = true; - } - /** - Destroys the shared object via a manual call to it's dtor. - */ - static void do_atexit() - { - if( m_destroyed ) return; - phoenix_CERR << "::do_atexit() @ " << std::hex << &instance() << std::endl; - static_cast<this_type &>(instance()).~Phoenix(); // will eventually trigger the BaseType dtor - } - - }; - template <typename T, typename C, typename I> bool Phoenix<T,C,I>::m_destroyed = false; + virtual ~Phoenix() + { + phoenix_CERR << "~Phoenix() @" << std::hex<< this << std::endl; + m_destroyed = true; + } + /** + Destroys the shared object via a manual call to it's dtor. + */ + static void do_atexit() + { + if( m_destroyed ) return; + phoenix_CERR << "::do_atexit() @ " << std::hex << &instance() << std::endl; + static_cast<this_type &>(instance()).~Phoenix(); // will eventually trigger the BaseType dtor + } + + }; + template <typename T, typename C, typename I> bool Phoenix<T,C,I>::m_destroyed = false; } // namespace |