Generic approach to multiple callbacks/event handlers

Started by
8 comments, last by Shannon Barber 22 years, 2 months ago
I''m big a fan of templates and generic programming, and dig it ever more as I learn more techniques. (Most recently I''ve started reading "Modern C++ Design" by Andrei Alexandrescu, which is Phenominal). I started writing a socket template library (KTL?) and the first vexing problem I''m come to is handling callbacks for events - things like OnAccept, OnConnect, OnClose, OnRecv, OnSendComplete, etc.. I first used an observer and called it''s virtual methods. While this works, I want to make the event handler a template parameter and inline the event handlers - much like the functor arguements of the STL algorithms. This would allow maximum flexiblity, as the user of the KTL will decide how the event handlers will work. (Later I''ll also add a template parameter for the ThreadingModel, but I want to get the simpler design working first). The show-stopping problem that I''ve run into, is that both of the template classes need to know each others type-infomation. The socket template takes the eventhandler class as a template parameter. The event handler class has methods that take the socket template class as parameters. Normally this could be solved by using a forward reference, but since I has template class that take each other as template parameters, it create a circular forward reference need. As such, this design is destined for the trash can... Does anyone know a generic technique for event handlers?
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Advertisement
I haven''t done anything specifically for generic event handlers, but I would imagine the event handler be implemented as a policy class.

If event handler class requires to call socket class methods, then it can take a template parameter that is instantiatied with the full definition the socket class.

Cross referencing template parameters should be fine.

Perhaps u want to post your design?
The answer to your question is something called "Unified Events".

I used to work at Microsoft with the VC++ compiler team. When I was there (and this was back in ''00), I was working on UE in addition to some other very interesting stuff. UE was supposed to ship with .NET (it was still called studio 7 at the time).

I would check MSDN for anything related to UE. If they scrapped the idea, they would likely remove the UE stuff from MSDN. If, on the otherhand, UE will appear in .NET then references should be there. .NET is scheduled to ship next month.

My gut feeling is they removed it from VC++7. Why? that''ll be the subject of a future anti-trust case for sure, but i digress.

Email me privately if you need more info. I''m always a little weary of the Microsoft monster.

~don

"They that can give up essential liberty to obtain temporary safety deserve neither liberty nor safety."
- Benjamin Franklin
www.ChippedDagger.com"They that can give up essential liberty to obtain temporary safety deserve neither." -- Benjamin Franklin"If opportunity doesn't knock, build a door." -- Milton Berle
Magmai :

Check out the book ''Pattern Hatching'' by John Vlissides

There is a very interesting discussion on Observer and something that grew out of that - Typed Message

There is some code there like this :

  template <class T>class Event{public:  class Handler  {  public:    Handler() { Event<T>::Register(this); }    virtual ~Handler() { Event<T>::Unregister(this; }    virtual void HandleEvent(const T& event) = 0;  };typedef std::list<Handler*> HandlerList;static void Register(Handler* handler) { mHandlers.push_back( handler ); }static void Unregister();static void Notify(Event<T>* t)  {  // call HandleEvent on all registered handlers  }void Notify() { T::Notify(this); }  


That should be enough to get you going along the right track.
although I had to alter the code as originally written to something more viable.


I even implemented a much more sophisticated/flexible version that I needed in order to have a generic Typed Message template.
Using a lot of the techniques presented in Alexandrescu''s book.

It works great with my C++ Builder v5 compiler. However VC++ 6 barfs at the template template parameters which are not supported

The book really explains it better than I could

Hope this helps

If you are really interested I could show you my final implementation with policy classes et. al.

( not that I really use it much since only C++ Builder can compile it and I don''t use that at work )
I guess I wasn''t think about how policy classes work correctly.

When you use an observer pattern you need to pass this along to the event handlers in order to use the same handler with multiple objects. With a policy class this is passed implicitly so I don''t need to pass it along.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
I''m not sure if I understand your comments.
No, I''m sure I don''t

The example code I showed there really has nothing to do with policy classes - my extended version of it uses policy classes.

I''m not sure about what is being passed when where and why, so I can''t really say anything one way or the other...

however - I realize i didn''t give example usage:

so here it is a:

  class MyEvent : public Event<MyEvent>{...};class HandlerOfMyEvent : public MyEvent::Handler{virtual void HandleEvent(const MyEvent& event);};  


Hopefully this helps clear up the usage and its implications.

Does this help?
Does this idea even apply to your situation?


Sorry SteveC, your example is precisely what I''m trying to avoid.

I want the user of my template class to be able to inline thier event handlers of my object, this will let them use an observer method if they choose to, but won''t force them to. Many times I''ve found myself using an observer (like your example) when what is observered never changes and there''s only one observer. As such the functions don''t really need to be virtual - you know at compile time which functions are going to be called, but the compiler doesn''t.

quote:Void
If event handler class requires to call socket class methods, then it can take a template parameter that is instantiatied with the full definition the socket class.

This is what I was trying to do, but it can''t be done in MSVC6 because it requires template template parameters. Otherwise you need to define the type of the policy using class before you can define the type of the policy - you need the type of the policy class in order to type the policy using class, ad infinitum... (At least I couldn''t figure out a way to make it compile.)
I can''t use any of the awesome TypeList solutions because there''s no partial template specialization.

This is what I have, and it''s far less than ideal (not usable in fact). It compiles and my handlers are inlined in release builds. But I can''t access the class that triggered the event from the event handler - even though they''re the same object!!! It''s because the policy (the event handler) class doesn''t know anything about the using class.

  template<class TEventHandler>class tcpClient : public tcpDataSocket, public TEventHandler, public Type<tcpClient<TEventHandler> >	{	protected:		TEventHandler m_EventHandler;	private:		Sync::CThread<tyThis> m_thrProcessIO;		UINT ProcessIO()			{			//...			case ConnectEvent:				{				//...				m_EventHandler.OnConnect();				}break;			}	};//The event handler classclass MyClientSocketEventHandler	{	public:		inline void OnConnect()			{			Debug::Console.Out("I''ve connected!\n");			//I''d really really like to be able to use the client socket (this) here			// But this class has no idea what the final object really is - it doesn''t know it''s a tcpClientSocket<>			Debug::Console.Out(this->boundAddress().c_str());			}	};  

Type is a meta-template that defines reflective typedefs, like tyThis, tyPointer, tyReferences, etc...
tcpDataSocket provides default implementation for Create, Close, and Send. There''s no Recv method, because the OnRecv event handler should be invoked (on a different thread) as soon as new data arrives, just like OnConnect should be invoked when the connect event signals.


I guess I could reorganize it so that you must derive a class from tcpClient<> to override the default event handler methods. Actaully, I think I have to.

- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
ah..screw VC++ 6..wait for VC++ 7..
Magmai:

Sorry this doesn''t meet your needs.

However, this is not a straight-up observer pattern.
It is an event model - events are pushed when appropriate - and are data specific.

So it is a little different then just getting the observer and querying the observer only to find nothing has changed.

the notify sends event with relevant data - so events only get sent when there is data to send.

I don''t understand how inlining makes handling the event optional or not - inheriting from the event class sets up handling of an event or not.

The case of one observer and nothing changing is kind of a specific example and not a ''generic'' event handling model.

I understand the desire to not have virtual functions so they can inline - however if the compiler knows the exact type at compile time ( eg has an actual object - not a pointer or a ref to one ) it can inline a virtual function

However, I doubt that VC++ succeeds in doing that.


This pattern is more based on the idea of focusing on the event, not its origin ( as observer does ) and having handlers as unrelated class types through multiple inheritance.

This example does not pass along any reference or pointer to the origin of the event.

----------------------

Looking at your code again from your last example -
I''m wondering - would generic functors help with the invocation of event handler methods?

Instead of calling methods on the handler directly invoke a generic functor which is passed into the constructor of the tcpClient class

That way the tcpClient class only needs to know the event handler type by NAME - eg pointer or reference, and does not directly invoke methods on it.

The m_EventHandler data member would have to be changed as well. And deciding who/where/how the handler gets constructed would likely have to change as well - maybe a policy class for that?

If you make inlined functors it might be able to inline everything...

I thought about using functors, and having a template parameter for each event, but I didn''t like the idea of having to create a new class for each event, as I can see the need to maintain state information between the event calls.

My goal is to have easily re-usable asyncronous socket classes, and I wanted to figure out a good way to signal the various events, because I run into the need for such a mechanism often.

So this is what it''s evolved to:

(These classes are actually nested inside of my test application class)
  class CTCPRecv : public Socket::tcpRecv<CTCPRecv>, public Socket::tcpDataSocketEvents, NameTag<std::string>	{	public:		void OnConnect();		void OnClose();		void OnSendComplete(Socket::tcpPacket* pPacket);		void OnReceive(Socket::tcpPacket* pPacket);		void OnError(const Socket::Error& e);	}; class CTCPServer : public Socket::tcpListener<CTCPServer, CTCPRecv>, public tcpListenerEvents<CTCPRecv>	{	public:		void OnError(Socket::Error e);					void OnAccept(tyRecvSocket* pRecv, Socket::tcpAddress* pRemoteAddr);	}; class CTCPClient : public Socket::tcpClient<CTCPClient>, public tcpDataSocketEvents, NameTag<std::string>	{	public:		void OnConnect();		void OnClose();		void OnSendComplete(Socket::tcpPacket* pPacket);		void OnReceive(Socket::tcpPacket* pPacket);		void OnError(const Socket::Error& e);	private:		u8 m_Chunk[4096];	}; CTCPServer m_tcpServer;CTCPClient m_tcpClient;  //Socket.hpptemplate<class TParent>class tcpClientSocket : public tcpDataSocket<TParent>	{	//When it''s time to invoke the connection event:	static_cast<TParent*>(this)->OnConnect();	}  


The tcp...Events class provide default implementation for all the required events. The tcp...<> socket templates won''t compile without all the event handlers provided, so the default event handlers let you make it compile and dump the events to the debug output. If someone has never used them before (or even me after a while of not using them) it should make it easier to understand and quick to get something compiling and connecting.

In the actual source I have the event handlers (the OnXXXX methods) inlined, but omitted that code for clarity.

I haven''t read the section on generic functors yet, but I bet they could be used for this situation. Once I get my hands on MSVC 7.1, I''ll look into it...
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

This topic is closed to new replies.

Advertisement