C++ listenable event source part 2

Posted: July 31, 2008 in Computer and Internet

(This is the continuation)

An event source could now simply instantiate and inherit this multiple times to become a source for multiple sources(!), to make the example smaller I present a class with one source (but with two events!).

   1: class DispatchablePipeIOListener
   2: {
   3: public:
   4:     virtual void onInput(class DispatchablePipe& pipe) = 0;
   5:     virtual void onOutput(class DispatchablePipe& pipe) = 0;
   6: };
   7:  
   8: class DispatchablePipe : public Pipe, protected AFW::IOEventHandler, public ListenableEventSource<DispatchablePipeIOListener>
   9: {
  10: public:
  11:     DispatchablePipe(void);
  12:     virtual ~DispatchablePipe(void);
  13:  
  14: protected:
  15:     typedef enum eContext
  16:     {
  17:         INPUT = 1,
  18:         OUTPUT = 2,
  19:     } Context;
  20:  
  21:     // AFW::IOEventHandler
  22:     virtual void handleInput(AFW_IO_HANDLE);
  23:     virtual void handleOutput(AFW_IO_HANDLE);
  24:     virtual AFW_IO_HANDLE getHandle();
  25:  
  26:     virtual int dispatchEvent(DispatchablePipeIOListener& l, const int& c, void* userParam);
  27:  
  28: };

Now a philosophical problem arises for me. Due to the fact that the template needs to know about the Listener interface (well it doesn’t really, but since I also want to avoid any type of casts it does, otherwise I could just void it..) I have to declare this outside the event source class (DispatchablePipe) , I normally like to put the source interface inside the source classes. The other annoying problem that arises is that because the event function is general I need to supply some context data to it, which I suppose is allright as long as it is a simple int, otherwise I need to delcare yet another class ouside the source… These problems wouldn’t be a problem if I instead would choose to aggregate the ListenerList above directly.

The beauty is of course that I get the add/removeListener functionality easily just by inheriting and providing a dispatchEvent trampoline. But I am not entirely sure it is worth the cost of extra complexity just to avoid typing addListener/removeListener and a member variable for each event type.

The event source looks internally like this

   1: void DispatchablePipe::handleInput(AFW_IO_HANDLE handle)
   2: {
   3:     fireNewEvent(INPUT);
   4: }
   5:  
   6: void DispatchablePipe::handleOutput(AFW_IO_HANDLE handle)
   7: {
   8:     fireNewEvent(OUTPUT);
   9: }
  10:  
  11: int DispatchablePipe::dispatchEvent(DispatchablePipeIOListener& l, const int& c, void* userParam)
  12: {
  13:     switch (c)
  14:     {
  15:     case OUTPUT:
  16:         l.onOutput(*this);
  17:         break;
  18:     case INPUT:
  19:         l.onInput(*this);
  20:         break;
  21:     }
  22:  
  23:     return 0;
  24: }
  25:  

If I instead chose to aggregate it would look like this (I inlined the entire class with empty stuff, but it is not important)

   1: class DispatchablePipe2 : public Pipe, protected AFW::IOEventHandler
   2: {
   3: public:
   4:     DispatchablePipe2(void) {};
   5:     virtual ~DispatchablePipe2(void) {};
   6:  
   7:     class IOListener
   8:     {
   9:     public:
  10:         virtual void onInput(DispatchablePipe2& pipe) = 0;
  11:         virtual void onOutput(DispatchablePipe2& pipe) = 0;
  12:     };
  13:  
  14:     int addListener(IOListener* listener, void* userParam = NULL)
  15:     {
  16:         return m_ioListeners.addListener(listener, userParam);
  17:     }
  18:  
  19:     int removeListener(IOListener* listener)
  20:     {
  21:         return m_ioListeners.removeListener(listener);
  22:     }
  23:  
  24: protected:
  25:     // AFW::IOEventHandler
  26:     virtual void handleInput(AFW_IO_HANDLE) {};
  27:     virtual void handleOutput(AFW_IO_HANDLE) {};
  28:     virtual AFW_IO_HANDLE getHandle() { getSelectableReadFd(); };
  29:  
  30:     typedef enum eEventType
  31:     {
  32:         INPUT = 1,
  33:         OUTPUT = 2,
  34:     } EventType;
  35:     typedef ListenerList<IOListener, DispatchablePipe2, EventType>        IOListenerCollection;
  36:     int dispatchEvent(IOListener& l, const EventType& c, void* userParam)
  37:     {
  38:         switch (c)
  39:         {
  40:         case INPUT:
  41:             l.onInput(*this);
  42:             break;
  43:         case OUTPUT:
  44:             l.onOutput(*this);
  45:             break;
  46:         }
  47:         return 0;
  48:     }
  49:  
  50:     int fireNewEvent(EventType e)
  51:     {
  52:         return m_ioListeners.visit(*this, &DispatchablePipe2::dispatchEvent, e);
  53:     }
  54:  
  55:     IOListenerCollection m_ioListeners;
  56: };

A few forward declaration are gone, the Context parameter is now typed to an enum and I can place the interface inside the source.

It would have been very neat if C++ would have allowed me to have incomplete types in the inheritance class for the class (as they will be complete when the class is fully read), but I am not aware of that that is possible, it would have been the best of both worlds.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s