Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members

phoenix.h

00001 #ifndef s11n_PHOENIX_H_INCLUDED 00002 #define s11n_PHOENIX_H_INCLUDED 1 00003 //////////////////////////////////////////////////////////////////////////////// 00004 // phoenix.h 00005 // phoenix<> provides "context singletons" with "phoexing" capabilities. 00006 // 00007 // Author: stephan beal <stephan@s11n.net> 00008 // License: Public Domain 00009 // CVS Revision: $Revision: 1.1.1.1 $ 00010 //////////////////////////////////////////////////////////////////////////////// 00011 00012 #include <stdlib.h> // atexit() 00013 00014 #ifndef PHOENIX_SINGLETON_DEBUG 00015 // enable debuggering to see when phoenixes are (re)created. 00016 #define PHOENIX_SINGLETON_DEBUG 0 00017 #endif 00018 00019 #include <iostream> 00020 #if PHOENIX_SINGLETON_DEBUG 00021 # include <s11n/cl_debuggering_macros.h> // COUT/CERR 00022 # include <typeinfo> 00023 # define PHOENIXCERR CERR << "phoenix<"<<typeid((base_type *)NULL).name()<<" , " << typeid((context_type *)NULL).name()<<"> " 00024 #else 00025 # define PHOENIXCERR if(0) std::cout 00026 #endif 00027 00028 00029 namespace s11n { 00030 00031 00032 /** 00033 Internal helper class to provide a default no-op 00034 initializer for phoenixed objects. 00035 00036 See the phoenix<> class. 00037 */ 00038 struct no_op_phoenix_initializer 00039 { 00040 /** Does nothing: This class is called no_op for a reason ;) */ 00041 template <typename T> 00042 void operator()( T & ) { return; } 00043 }; 00044 00045 /** 00046 phoenix is class for holding singleton-style instances of 00047 BaseType objects. Rather than requiring that BaseType be a 00048 Singleton type, phoenix subclasses BaseType to add the 00049 phoenix-like capabilities. Phoenixing makes the shared 00050 object post-main() safe, in terms of object destruction 00051 order. 00052 00053 Parameterized on: 00054 00055 - BaseType: must be struct or class type and must be 00056 default-constructable. i have no clue what is supposed to 00057 happen if BaseType's dtor is not virtual. That said, 00058 phoenix has been successfully demonstrated with a BaseType 00059 of std::map, which has no virtual dtor. 00060 00061 - ContextType: These objects are only singletons within the 00062 given ContextType. That is, phoenix&lt;T,X&gt;::instance() 00063 will return a different object than phoenix&lt;T,Y&gt; 00064 will. 00065 00066 - InitializerType: must be a unary functor accepting a 00067 BaseType &. It's return value is ignored. The default 00068 functor does nothing. The InitializerType is called when a 00069 to-be-phoenixed object is initially created and whenever it 00070 is phoenixed. This is intended to be used, e.g., for 00071 re-populating a phoenixed shared object. 00072 TODO: investigate the implications of a predicate 00073 initializer, which would return false if the object could 00074 not be initialized. 00075 00076 00077 Whether or not BaseType is technically a singleton depends 00078 on entirely BaseType itself. This class is more often used 00079 to provide easy access to context-dependent shared objects, 00080 rather than pure singletons. The phoenix class itself is a 00081 true Singleton, but each combination of template arguments 00082 provides a different Singleton *type*, so the end effect is 00083 "context singletons." 00084 00085 00086 This is another attempt to solve the classic 00087 Keyboard-Console-Log problem, as discussed at length in 00088 <i>Modern C++ Design</i>. It relies on sane behaviour in 00089 the C library's atexit() function, which, as is shown in 00090 MC++D, is not the case on all systems. That said, the 00091 phoenix-specific behaviours are undefined on those systems, 00092 which is only to say that it might not be post-main() safe. 00093 00094 00095 Caveats: 00096 00097 i am not 100% clear on all of the implications of this 00098 implementation's approach... my gut tells me i'm missing 00099 some significant bits. i mean, it <i>can't</i> have been 00100 this straightforward to solve ;). The very nature of the 00101 Phoenix Singleton problem makes it difficult to reliably 00102 test in real-world applications. That said, i have seen a 00103 objects be successfully phoenixed and atexit()ed, so it is 00104 known to at least "basically" work. 00105 00106 There's a paper about "context singletons", this class, 00107 and some of it's implications, at: 00108 00109 http://s11n.net/misccode/context_singletons.html 00110 00111 00112 [Much later: i've gotten more re-use out of this class than 00113 probably any other single class i've ever written.] 00114 */ 00115 template < 00116 typename BaseType, 00117 typename ContextType = BaseType, 00118 typename InitializerType = no_op_phoenix_initializer 00119 > 00120 struct phoenix : public BaseType 00121 { 00122 /** 00123 context_type is unused by this class, but might be useful 00124 for type identification at some point. 00125 */ 00126 typedef ContextType context_type; 00127 /** 00128 The BaseType parameterized type. 00129 */ 00130 typedef BaseType base_type; 00131 00132 /** 00133 The functor type used to initialize this phoenixed object. 00134 */ 00135 typedef InitializerType initializer_type; 00136 00137 /** 00138 Returns a shared instance of this object. 00139 00140 The instance() method will always return the same 00141 address, though it is potentially possible 00142 (post-main()) that the actual object living at that 00143 address is different from previous calls. 00144 00145 It is never a good idea to hold on to the returned 00146 reference for the life of an object, as that bypasses 00147 the phoenixing capabilities. 00148 00149 If you ever delete it you're on you're own. That's 00150 a Bad Idea. 00151 */ 00152 static base_type & instance() 00153 { 00154 static this_type meyers; 00155 static bool donethat = false; 00156 if( this_type::m_destroyed ) 00157 { 00158 PHOENIXCERR << "Phoenixing!" << std::endl; 00159 donethat = false; 00160 new( &meyers ) this_type; 00161 atexit( this_type::do_atexit ); 00162 } 00163 if( !donethat ) 00164 { 00165 PHOENIXCERR << "initializing instance" << std::endl; 00166 donethat = true; 00167 initializer_type()( meyers ); 00168 } 00169 PHOENIXCERR << "instance() == " <<std::hex<<&meyers<<std::endl; 00170 return meyers; 00171 } 00172 00173 private: 00174 00175 /** A convenience typedef. */ 00176 typedef phoenix<base_type,context_type,initializer_type> this_type; 00177 00178 static bool m_destroyed; 00179 00180 phoenix() 00181 { 00182 PHOENIXCERR << "phoenix() @" << std::hex<< this << std::endl; 00183 m_destroyed = false; 00184 } 00185 00186 ~phoenix() 00187 { 00188 PHOENIXCERR << "~phoenix() @" << std::hex<< this << std::endl; 00189 m_destroyed = true; 00190 } 00191 /** 00192 Destroys the shared object via a manual call to it's dtor. 00193 */ 00194 static void do_atexit() 00195 { 00196 if( m_destroyed ) return; 00197 PHOENIXCERR << "::do_atexit() @ " << std::hex << &instance() << std::endl; 00198 static_cast<this_type &>(instance()).~phoenix(); // will eventually trigger the BaseType dtor 00199 } 00200 00201 }; 00202 template <typename T, typename C, typename I> bool phoenix<T,C,I>::m_destroyed = false; 00203 00204 } // namespace 00205 00206 #undef PHOENIX_SINGLETON_DEBUG 00207 #undef PHOENIXCERR 00208 #endif // s11n_PHOENIX_H_INCLUDED 00209 00210 00211 /********************************************************************************* 00212 todo: check this out more closely: 00213 00214 Program received signal SIGSEGV, Segmentation fault. 00215 0x4015e2d0 in std::string::compare(std::string const&) const () from /usr/lib/libstdc++.so.5 00216 (gdb) bt 00217 #0 0x4015e2d0 in std::string::compare(std::string const&) const () from /usr/lib/libstdc++.so.5 00218 #1 0x0807eae2 in operator< <char, std::char_traits<char>, std::allocator<char> > (__lhs=@0x80a1b30, __rhs=@0xbfffec10) 00219 at basic_string.h:948 00220 #2 0x0807eac4 in std::less<std::string>::operator()(std::string const&, std::string const&) const (this=0x4008cad0, 00221 __x=@0x80a1b30, __y=@0xbfffec10) at stl_function.h:197 00222 #3 0x40064b34 in std::_Rb_tree<std::string, std::pair<std::string const, s11n::basic_serializer* (*)()>, std::_Select1st<std::pair<std::string const, s11n::basic_serializer* (*)()> >, std::less<std::string>, std::allocator<std::pair<std::string const, s11n::basic_serializer* (*)()> > >::find(std::string const&) (this=0x4008cac8, __k=@0xbfffec10) at stl_tree.h:1271 00223 #4 0x400647ff in std::map<std::string, s11n::basic_serializer* (*)(), std::less<std::string>, std::allocator<std::pair<std::string const, s11n::basic_serializer* (*)()> > >::find(std::string const&) (this=0x4008cac8, __x=@0xbfffec10) at stl_map.h:468 00224 #5 0x4006477b in s11n::instantiator<s11n::basic_serializer, std::string>::instantiate(std::string const&) (key=@0xbfffec10) 00225 at instantiator.h:151 00226 #6 0x40064712 in s11n::class_loader<s11n::basic_serializer, std::string, false>::load(std::string const&) const (this=0x4008caf0, 00227 key=@0xbfffede0) at class_loader.h:298 00228 #7 0x40067f6e in s11n::serializer_loader::load(std::string const&) const (this=0x4008caf0, key=@0xbfffede0) 00229 at serializer_loader.cpp:31 00230 #8 0x40063454 in s11n::node_loader::load(std::istream&) const (this=0x4008cad4, is=@0x80cf5d0) at node_loader.cpp:69 00231 #9 0x400639ea in s11n::node_loader::load(std::string const&) const (this=0x4008cad4, key=@0xbfffef10) at node_loader.cpp:95 00232 #10 0x400632e3 in s11n::node_loader::load_node(std::string const&) (key=@0xbfffef10) at node_loader.cpp:40 00233 #11 0x40060007 in s11n::libconfig_initializer::operator()(s11n::libconfig&) (this=0xbfffef67, conf=@0x4008ca60) at libconfig.cpp:53 00234 #12 0x4005fda3 in s11n::phoenix<s11n::libconfig, s11n::libconfig, s11n::libconfig_initializer>::instance() () at phoenix.h:152 00235 #13 0x4005fb53 in s11n::config() () at libconfig.cpp:65 00236 #14 0x40069327 in s11n::basic_serializer::use_indentation() const (this=0xbffff0d0) at serializer.cpp:99 00237 #15 0x4006957e in s11n::funtxt_serializer::serialize(s11n::s11n_node const&, std::ostream&) const (this=0xbffff0d0, 00238 node=@0x4008ca60, os=@0x80a4350) at serializer.cpp:153 00239 #16 0x08073532 in s11n::s11n_io<s11n::funtxt_serializer>::save(s11n::s11n_node const&, std::ostream&) (node=@0x4008ca60, 00240 os=@0x80a4350) at s11n_io.h:77 00241 #17 0x400605d7 in s11n::s11n_io<s11n::funtxt_serializer>::save(s11n::s11n_node const&, std::string const&) (node=@0x4008ca60, 00242 filename=@0x4008ca84) at s11n_io.h:94 00243 #18 0x400603ac in ~libconfig (this=0x4008ca60) at libconfig.cpp:33 00244 #19 0x40060153 in ~phoenix (this=0x4008ca60) at phoenix.h:174 00245 #20 0x4005fc63 in __tcf_0 () at phoenix.h:140 00246 #21 0x401ee010 in exit () from /lib/i686/libc.so.6 00247 #22 0x401d7d1f in __libc_start_main () from /lib/i686/libc.so.6 00248 #23 0x0806d4b1 in _start () at ../sysdeps/i386/elf/start.S:102 00249 00250 00251 *********************************************************************************/ 00252

Generated on Wed Jul 28 16:04:14 2004 for s11n by doxygen 1.3.7