Igor R wrote:
We took the opposite approach of explicitly queuing event objects. When we "pump" the queue, it sends each of those pending events through the signal to all connected slots.
Could you please elaborate a bit more or just post a short code snippet?
I don't think I'm allowed to post a snippet of the real code. ;-)
You queued functors containing shared_ptr to the original signal objects?
No -- or at least it doesn't sound to me like a match. Here's what we do. Bear in mind that this is a single thread using classic Boost.Signals. Let's say that Event is the data object we intend to propagate through the system. Further suppose this typedef: typedef boost::signal<void(const Event&)> MySignal; We defined an EventQueue object containing an instance of MySignal and (I believe) a std::vector<Event>. We gave EventQueue a method addListener(const MySignal::slot_type&) that just forwards to MySignal::connect(). EventQueue also has a method queueEvent(const Event&) that forwards to std::vector<Event>::push_back(); finally it has a pump() method that I'll describe in a moment. So during initialization, various listeners locate this EventQueue and call its addListener(), typically using boost::bind() to bind a member function of some specific listener-class instance. During normal operation, components call queueEvent(someEvent), which simply adds a copy of someEvent to the EventQueue's std::vector. Once per frame, the controller calls pump(), which iterates through the std::vector<Event>, passing each Event to the MySignal instance. When done, it clears the vector.
You used some generic approach or you made a specific "subscription" function for each type of notification?
Heh -- our app is a hybrid of C++ and Python. We use Boost.Python's convenient dict wrapper to embed a Python dict in our Event object. So you can instantiate an Event (and call queueEvent() with it) in either Python or C++, and any such caller can embed any information it wants in the Event. I realize there's a great controversy raging about free-form events vs. a rigid event hierarchy. Without jumping on a soapbox here, I'll just say that the free-form model works well for us. It does allow us to capture Event objects by copying. If we used polymorphic Events, I guess we could have used a std::vector< boost::shared_ptr<Event> > instead. Anyway, we found the generic approach simple and manageable.