glpublisher.h

00001 /*
00002 Copyright (c) 2000-2003 Lee Thomason (www.grinninglizard.com)
00003 Grinning Lizard Utilities.
00004 
00005 This software is provided 'as-is', without any express or implied 
00006 warranty. In no event will the authors be held liable for any 
00007 damages arising from the use of this software.
00008 
00009 Permission is granted to anyone to use this software for any 
00010 purpose, including commercial applications, and to alter it and 
00011 redistribute it freely, subject to the following restrictions:
00012 
00013 1. The origin of this software must not be misrepresented; you must 
00014 not claim that you wrote the original software. If you use this 
00015 software in a product, an acknowledgment in the product documentation 
00016 would be appreciated but is not required.
00017 
00018 2. Altered source versions must be plainly marked as such, and 
00019 must not be misrepresented as being the original software.
00020 
00021 3. This notice may not be removed or altered from any source 
00022 distribution.
00023 */
00024 
00025 
00026 #ifndef GRINLIZ_PUBLISHER_INCLUDED
00027 #define GRINLIZ_PUBLISHER_INCLUDED
00028 
00029 #ifdef _MSC_VER
00030 #pragma warning( disable : 4786 )
00031 #endif
00032 
00033 #include <set>
00034 
00035 namespace grinliz {
00036 
00052 template< class LISTENER_CLASS >
00053 class Publisher
00054 {
00055   public:
00056 
00057     typedef typename std::set<LISTENER_CLASS*>::iterator iterator;
00059     typedef typename std::set<LISTENER_CLASS*>::const_iterator const_iterator;
00060      
00061         Publisher() : copyValid( false )        {}
00062           
00063         ~Publisher()
00064         {
00065                 for(    iterator listenerIterator = listeners.begin();
00066                                 listenerIterator != listeners.end();
00067                                 ++listenerIterator )
00068                 {
00069                         (*listenerIterator)->publishers.erase( this );
00070                 }
00071         }
00072 
00074         void AddListener( LISTENER_CLASS* add )                 
00075         { 
00076                 copyValid = false;
00077                 listeners.insert( add ); 
00078                 add->publishers.insert( this );
00079         }
00080 
00083         void RemoveListener( LISTENER_CLASS* remove )   
00084         { 
00085                 copyValid = false;
00086                 remove->publishers.erase( this );
00087                 listeners.erase( remove );
00088         }
00089  
00101     const_iterator begin()      {       if ( !copyValid )
00102                                                                 {
00103                                                                         listenersCopy.clear();
00104                                                                         listenersCopy = listeners;
00105                                                                         copyValid = true;
00106                                                                 }
00107                                                                 return listenersCopy.begin(); 
00108                                                         }
00109 
00111     const_iterator end()      { return listenersCopy.end(); }
00112 
00113         bool empty() { return listeners.empty(); }
00114  
00115   private:
00116         std::set<LISTENER_CLASS*> listeners;
00117         // Okay, it's very natural to want to Add/Remove while iterating
00118         // through the set. Even with documentation, it's just too easy
00119         // a mistake to make. Changing the set while iterating will 
00120         // crash and invalidate the     set. To overcome this, begin() makes a copy.
00121         // But we keep a flag around so this doesn't happen unless it needs to.
00122         bool copyValid;
00123         std::set<LISTENER_CLASS*> listenersCopy;
00124 };
00125 
00126 
00149 template <class LISTENER_CLASS>
00150 class Listener
00151 {
00152         friend class Publisher< LISTENER_CLASS >;
00153 
00154   private:
00155         std::set< Publisher<LISTENER_CLASS>* > publishers;
00156     typedef typename std::set< Publisher<LISTENER_CLASS>* >::iterator iterator;
00157 
00158   public:
00159         virtual ~Listener()
00160         {
00161             // This a strange bit of code. The RemoveListener call
00162             // will result the Publisher class reaching into this class
00163             // to erase the Publisher*. Therefore, the iterator will 
00164             // continually be invalidated; hence always use begin() and 
00165             // break if end().
00166             //
00167             // The other odd bit of code is the back-cast to our child
00168             // type - LISTENER_CLASS. Sleazy c++ trick.
00169             //
00170                 while ( true )
00171                 {
00172                         iterator it = publishers.begin();
00173                         if ( it == publishers.end() ) break;
00174                         (*it)->RemoveListener( static_cast< LISTENER_CLASS* >( this ) );
00175                 }
00176         }
00177 };
00178 
00179 };      // namespace grinliz
00180 #endif

Generated on Thu Jul 20 20:45:31 2006 for Kyra by  doxygen 1.4.7