Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

phoenix.hpp

Go to the documentation of this file.
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&lt;T,X&gt;::instance()
00069            will return a different object than phoenix&lt;T,Y&gt;
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

Generated on Fri Nov 25 17:19:29 2005 for libs11n-1.2.0 by  doxygen 1.4.4