NewNet/nnevent.h

00001 /*  NewNet - A networking framework in C++
00002     Copyright (C) 2006 Ingmar K. Steen (iksteen@gmail.com)
00003 
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018  */
00019 
00020 #ifndef NEWNET_EVENT_H
00021 #define NEWNET_EVENT_H
00022 
00023 #include "nnobject.h"
00024 #include "nnrefptr.h"
00025 #include <vector>
00026 #include <functional>
00027 #include <algorithm>
00028 
00029 namespace NewNet
00030 {
00032 
00035   template<typename T> class Event : public Object
00036   {
00037   public:
00039 
00041     class Callback : public Object
00042     {
00043     public:
00045       Callback() { }
00046 
00048       virtual ~Callback() { }
00049 
00051 
00052       virtual void operator()(T t) = 0;
00053 
00054 #ifndef DOXYGEN_UNDOCUMENTED
00055       /* Callback was added to an Event */
00056       void onConnected(Event * event)
00057       {
00058         m_Events.push_back(event);
00059       }
00060 
00061       /* Callback was disconnected from an Event */
00062       void onDisconnected(Event * event)
00063       {
00064         m_Events.erase(std::find(m_Events.begin(), m_Events.end(), event));
00065       }
00066 #endif // DOXYGEN_UNDOCUMENTED
00067 
00069 
00073       void disconnect()
00074       {
00075         /* Artificially increase our reference count. Otherwise we might get
00076            prematurely deleted when we get disconnected from the last Event */
00077         ++(this->refCounter());
00078 
00079         /* Disconnect from all the events we're registered to */
00080         while(! m_Events.empty())
00081           m_Events.front()->disconnect(this);
00082 
00083         /* Decrease the refrence count again and delete if necessary */
00084         if(--(this->refCounter()))
00085           delete this;
00086       }
00087 
00088     private:
00089       /* Private copy constructor, you don't want this happening */
00090       Callback(const Callback &) { }
00091       std::vector<Event *> m_Events;
00092     };
00093 
00094   private:
00095 #ifndef DOXYGEN_UNDOCUMENTED
00096     /* Callback to a method of a NewNet::Object */
00097     template<class ObjectType, typename MethodType> class BoundCallback : public Callback
00098     {
00099     private:
00100       /* We use this to track deletion of our Object */
00101       class GuardObjectCallback : public GuardObject::Callback
00102       {
00103       public:
00104         GuardObjectCallback(BoundCallback * callback) : m_Callback(callback)
00105         {
00106         }
00107 
00108         /* If the Object is destroyed, notify our Callback */
00109         void operator()(Object *)
00110         {
00111           m_Callback->onObjectDeleted();
00112         }
00113       private:
00114         BoundCallback * m_Callback;
00115       };
00116       GuardObjectCallback * m_GuardObjectCallback;
00117 
00118     public:
00119       BoundCallback(ObjectType * object, MethodType method)
00120                    : m_Object(object), m_Method(method)
00121       {
00122         /* Register to the Object's delete guard */
00123         m_GuardObjectCallback = new GuardObjectCallback(this);
00124         m_Object->guardObject() += m_GuardObjectCallback;
00125       }
00126 
00127       ~BoundCallback()
00128       {
00129         /* If the object is still valid, remove our delete callback from
00130            its delete guard */
00131         if(m_Object)
00132           m_Object->guardObject() -= m_GuardObjectCallback;
00133 
00134         delete m_GuardObjectCallback;
00135       }
00136 
00137       void operator()(T t)
00138       {
00139         /* Since C++ methods are internally called as
00140            Class::Method(object, ...), this results in a valid C++ bound
00141            method call. */
00142         if(m_Object)
00143           std::bind1st(std::mem_fun(m_Method), m_Object)(t);
00144       }
00145 
00146     protected:
00147       /* Object we're bound to was deleted, reset pointer and disconnect
00148          from all Events we're registered to */
00149       void onObjectDeleted()
00150       {
00151         m_Object = 0;
00152         Callback::disconnect();
00153       }
00154 
00155     private:
00156       /* Private copy constructor, you don't want this happening */
00157       BoundCallback(const BoundCallback &) { }
00158 
00159       ObjectType * m_Object;
00160       MethodType m_Method;
00161     };
00162 #endif // DOXYGEN_UNDOCUMENTED
00163 
00164   public:
00166 
00167     Event() { }
00168 
00170 
00172     Event(const Event & that)
00173     {
00174       typename std::vector<RefPtr<Callback> >::const_iterator it, end;
00175       end = that.m_Callbacks.end();
00176       for(it = that.m_Callbacks.begin(); it != end; ++it)
00177         connect(*it);
00178     }
00179 
00181 
00182     virtual ~Event()
00183     {
00184       /* Disconnect all Callbacks */
00185       clear();
00186     }
00187 
00189 
00193     void clear()
00194     {
00195       while(! m_Callbacks.empty())
00196         disconnect(m_Callbacks.front());
00197     }
00198 
00200 
00202     Callback * connect(Callback * callback)
00203     {
00204       /* Push a RefPtr to the Callback to our list */
00205       m_Callbacks.push_back(callback);
00206 
00207       /* Notify the Callback that it's connected to us */
00208       callback->onConnected(this);
00209 
00210       return callback;
00211     }
00212 
00214 
00216     template<class ObjectType, typename MethodType>
00217     static Callback * bind(ObjectType * object, MethodType method)
00218     {
00219       return new BoundCallback<ObjectType, MethodType>(object, method);
00220     }
00221 
00223 
00226     template<class ObjectType, typename MethodType>
00227     Callback * connect(ObjectType * object, MethodType method)
00228     {
00229       return connect(bind(object, method));
00230     }
00231 
00233 
00236     void disconnect(Callback * callback)
00237     {
00238       /* Artificially increase the reference count so it doesn't get deleted
00239          prematurely */
00240       ++(callback->refCounter());
00241 
00242       /* Delete the Callback from our list */
00243       m_Callbacks.erase(std::find(m_Callbacks.begin(), m_Callbacks.end(), callback));
00244 
00245       /* Notify the Callback it was disconnected */
00246       callback->onDisconnected(this);
00247 
00248       /* Decrease the reference count of the Callback and delete if
00249          necessary */
00250       if(--(callback->refCounter()))
00251         delete callback;
00252     }
00253 
00255 
00257     void operator()(T t)
00258     {
00259       std::vector<RefPtr<Callback> > callbacks(m_Callbacks);
00260       typename std::vector<RefPtr<Callback> >::iterator it, end = callbacks.end();
00261       for(it = callbacks.begin(); it != end; ++it)
00262       {
00263         (*(*it))(t);
00264       }
00265     }
00266 
00267   private:
00268     std::vector<RefPtr<Callback> > m_Callbacks;
00269   };
00270 }
00271 
00272 #endif // NEWNET_EVENT_H

Generated on Sun Jan 7 14:00:01 2007 for NewNet by  doxygen 1.5.1