00001 #ifndef s11n_net_s11n_PHOENIX_HPP_INCLUDED 00002 #define s11n_net_s11n_PHOENIX_HPP_INCLUDED 1 00003 //////////////////////////////////////////////////////////////////////////////// 00004 // phoenix.hpp 00005 // phoenix<> provides "context singletons" with "phoenixing" capabilities. 00006 // 00007 // Author: stephan beal <stephan@s11n.net> 00008 // License: Public Domain 00009 // CVS Revision: $Revision: 1.4 $ 00010 //////////////////////////////////////////////////////////////////////////////// 00011 00012 #include <stdlib.h> // atexit() 00013 #include <iostream> // cout/cerr 00014 00015 00016 #ifndef phoenix_DEBUG 00017 // enable debuggering to see when phoenixes are (re)created. 00018 # define phoenix_DEBUG 0 00019 #endif 00020 00021 #if phoenix_DEBUG 00022 # include <typeinfo> 00023 # define phoenix_CERR std::cerr << __FILE__ << ":" << std::dec << __LINE__ << " : " \ 00024 << "phoenix<"<<typeid((base_type *)NULL).name()<<" , " \ 00025 << typeid((context_type *)NULL).name()<<"> " 00026 #else 00027 # define phoenix_CERR if(0) std::cerr 00028 #endif // phoenix_DEBUG 00029 00030 namespace s11n { namespace Detail { 00031 00032 00033 /** 00034 Internal helper class to provide a default no-op 00035 initializer for phoenixed objects. 00036 00037 See the phoenix<> class. 00038 */ 00039 struct no_op_phoenix_initializer 00040 { 00041 /** Does nothing: This class is called no_op for a reason ;) */ 00042 template <typename T> 00043 void operator()( T & ) { return; } 00044 }; 00045 00046 /** 00047 00048 The phoenix class acts as a wrapper for adding "phoenixing" 00049 behaviour to arbitrary shared objects, as covered in detail in 00050 Alexandrescu's "Modern C++ Design". 00051 00052 phoenix is a class for holding singleton-style instances of 00053 BaseType objects. Rather than requiring that BaseType be a 00054 Singleton type, phoenix subclasses BaseType to add the 00055 phoenix-like capabilities. Phoenixing makes the shared 00056 object post-main() safe, in terms of object destruction 00057 order. 00058 00059 Parameterized on: 00060 00061 - BaseType: must be struct or class type and must be 00062 default-constructable. i have no clue what is supposed to 00063 happen if BaseType's dtor is not virtual. That said, 00064 phoenix has been successfully demonstrated with a BaseType 00065 of std::map, which has no virtual dtor. 00066 00067 - ContextType: These objects are only singletons within the 00068 given ContextType. That is, phoenix<T,X>::instance() 00069 will return a different object than phoenix<T,Y> 00070 will. 00071 00072 - InitializerType: must be a unary functor accepting a 00073 BaseType &. It's return value is ignored. The default 00074 functor does nothing. The InitializerType is called when a 00075 to-be-phoenixed object is initially created and whenever it 00076 is phoenixed. This is intended to be used, e.g., for 00077 re-populating a phoenixed shared object. 00078 TODO: investigate the implications of a predicate 00079 initializer, which would return false if the object could 00080 not be initialized. 00081 00082 00083 Whether or not BaseType is technically a singleton depends 00084 on entirely BaseType itself. This class is more often used 00085 to provide easy access to context-dependent shared objects, 00086 rather than pure singletons. The phoenix class itself is a 00087 true Singleton, but each combination of template arguments 00088 provides a different Singleton *type*, so the end effect is 00089 "context singletons." 00090 00091 00092 This is another attempt to solve the classic 00093 Keyboard-Console-Log problem, as discussed at length in 00094 <i>Modern C++ Design</i>. It relies on sane behaviour in 00095 the C library's atexit() function, which, as is shown in 00096 MC++D, is not the case on all systems. That said, the 00097 phoenix-specific behaviours are undefined on those systems, 00098 which is only to say that it might not be post-main() safe. 00099 00100 00101 Caveats: 00102 00103 i am not 100% clear on all of the implications of this 00104 implementation's approach... my gut tells me i'm missing 00105 some significant bits. i mean, it <i>can't</i> have been 00106 this straightforward to solve ;). The very nature of the 00107 Phoenix Singleton problem makes it difficult to reliably 00108 test in real-world applications. That said, i have seen a 00109 objects be successfully phoenixed and atexit()ed, so it is 00110 known to at least "basically" work. 00111 00112 There's a paper about "context singletons", this class, 00113 and some of it's implications, at: 00114 00115 http://s11n.net/papers/ 00116 00117 00118 [Much later: i've gotten more re-use out of this class than 00119 probably any other single class i've ever written.] 00120 */ 00121 template < 00122 typename BaseType, 00123 typename ContextType = BaseType, 00124 typename InitializerType = no_op_phoenix_initializer 00125 > 00126 struct phoenix : public BaseType 00127 { 00128 /** 00129 context_type is unused by this class, but might be useful 00130 for type identification at some point. 00131 */ 00132 typedef ContextType context_type; 00133 /** 00134 The BaseType parameterized type. 00135 */ 00136 typedef BaseType base_type; 00137 00138 /** 00139 The functor type used to initialize this phoenixed object. 00140 */ 00141 typedef InitializerType initializer_type; 00142 00143 /** 00144 Returns a shared instance of this object. 00145 00146 The instance() method will always return the same 00147 address, though it is potentially possible 00148 (post-main()) that the actual object living at that 00149 address is different from previous calls. 00150 00151 It is never a good idea to hold on to the returned 00152 reference for the life of an object, as that bypasses 00153 the phoenixing capabilities. 00154 00155 If you ever delete it you're on you're own. That's 00156 a Bad Idea. 00157 */ 00158 static base_type & instance() 00159 { 00160 static this_type meyers; 00161 static bool donethat = false; 00162 if( this_type::m_destroyed ) 00163 { 00164 phoenix_CERR << "Phoenixing!" << std::endl; 00165 donethat = false; 00166 new( &meyers ) this_type; 00167 atexit( this_type::do_atexit ); 00168 } 00169 if( !donethat ) 00170 { 00171 phoenix_CERR << "initializing instance" << std::endl; 00172 donethat = true; 00173 initializer_type()( meyers ); 00174 } 00175 phoenix_CERR << "instance() == " <<std::hex<<&meyers<<std::endl; 00176 return meyers; 00177 } 00178 00179 private: 00180 00181 /** A convenience typedef. */ 00182 typedef phoenix<base_type,context_type,initializer_type> this_type; 00183 00184 static bool m_destroyed; 00185 00186 phoenix() 00187 { 00188 phoenix_CERR << "phoenix() @" << std::hex<< this << std::endl; 00189 m_destroyed = false; 00190 } 00191 00192 virtual ~phoenix() throw() 00193 { 00194 phoenix_CERR << "~phoenix() @" << std::hex<< this << std::endl; 00195 m_destroyed = true; 00196 } 00197 /** 00198 Destroys the shared object via a manual call to it's dtor. 00199 */ 00200 static void do_atexit() 00201 { 00202 if( m_destroyed ) return; 00203 phoenix_CERR << "::do_atexit() @ " << std::hex << &instance() << std::endl; 00204 static_cast<this_type &>(instance()).~phoenix(); // will eventually trigger the BaseType dtor 00205 } 00206 00207 }; 00208 template <typename T, typename C, typename I> bool phoenix<T,C,I>::m_destroyed = false; 00209 00210 } } // namespace 00211 00212 #undef phoenix_DEBUG 00213 #undef phoenix_CERR 00214 #endif // s11n_net_s11n_PHOENIX_HPP_INCLUDED