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 Tue Oct 26 18:25:59 2004 for s11n by  doxygen 1.3.9.1