title: Designing an event mechanism in C++
body: <p>I am trying to design a generic (but somewhat use-case-specific) event-passing mechanism in C++ without going against the grain with regard to "new style" C++, and at the same time without going overboard with templates.</p>
  
  <p>My use case is somewhat particular in that I require complete control over when events are distributed. The event system underlies a world simulation where each iteration of the world acts on the events generated by the previous frame. So I require all events to be queued up before they are dispatched, so that the app can flush the queue at specific intervals, somewhat like the classic GUI event loop.</p>
  
  <p>My use case is trivial to implement in Ruby, Python or even C, but with C++ I am coming up a bit short. I <em>have</em> looked at Boost::Signal and other similar libraries, but they seem too complex or inflexible to suit my particular use case. (Boost, in particular, is templated-based often to the point of utter confusion, especially things like boost::bind or boost::function.)</p>
  
  <hr>
  
  <h3>Here's the system, in broad strokes:</h3>
  
  <ul>
  <li><p>Consumers listen to events by registering themselves directly with the objects that produce the events.</p></li>
  <li><p>Events are just string names, but each event may have additional data attached to it.</p></li>
  <li><p>Listeners are just methods. If this were C++11 I would use lambdas, but I need broad compiler portability, so using methods for the moment.</p></li>
  <li><p>When a producer triggers an event, the event goes into the queue until the time comes to dispatch it to the list of listeners.</p></li>
  <li><p>The queue is dispatched in strict order of event triggering. (So if producer A triggers event x, an producer B triggers y, and producer B triggers z again, then the total order is x, y, z.)</p></li>
  <li><p>It's important that whatever events are produced by the listeners during will not be dispatched until the <em>next</em> iteration -- so there are really two queues internally.</p></li>
  </ul>
  
  <hr>
  
  <h3>Here's an "ideal" pseudo-code example of a consumer:</h3>
  
  <pre><code>SpaceshipController::create() {
      spaceship.listen("crash", &amp;on_crash);
  }
  
  SpaceshipController::on_crash(CrashEvent event) {
      spaceship.unlisten("crash", &amp;on_crash);
      spaceship.remove();
      add(new ExplosionDebris);
      add(new ExplosionSound);
  }
  </code></pre>
  
  <p>And here is a producer:</p>
  
  <pre><code>Spaceship::collided_with(CollisionObject object) {
      trigger("crash", new CrashEvent(object));
  }
  </code></pre>
  
  <p>All this is well and good, but translating into modern C++ is where I meet difficulty.</p>
  
  <hr>
  
  <p>The problem is that either one has to go with old-style C++ with casting polymorphic instances and ugliness, or one has to go with template-level polymorphism with compile-time defined typing.</p>
  
  <p>I have experimented with using boost::bind(), and I can produce a listen method like this:</p>
  
  <pre><code>class EventManager
  {
      template &lt;class ProducerClass, class ListenerClass, class EventClass&gt;
          void EventManager::listen(
              shared_ptr&lt;ProducerClass&gt; producer,
              string event_name,
              shared_ptr&lt;ListenerClass&gt; listener,
              void (ListenerClass::*method)(EventClass* event)
          )
      {
          boost::function1&lt;void, EventClass*&gt; bound_method = boost::bind(method, listener, _1);
          // ... add handler to a map for later execution ...
      }
  }
  </code></pre>
  
  <p>(Note how I am defining central event manager; that is because I need to maintain a single queue across all producers. For convenience, individual classes still inherit a mixin that provides listen() and trigger() that delegate to the event manager.)</p>
  
  <p>Now it's possible to listen by doing:</p>
  
  <pre><code>void SpaceshipController::create()
  {
      event_manager.listen(spaceship, "crash", shared_from_this(),
          &amp;SpaceshipController::on_crash);
  }
  
  void SpaceshipController::on_crash(CrashEvent* event)
  {
      // ...
  }
  </code></pre>
  
  <p>That's pretty good, although it's verbose. I hate forcing every class to inherit enable_shared_from_this, and C++ requires that method references include the class name, which sucks, but both problems are probably unavoidable.</p>
  
  <p>Unfortunately, I don't see how to implement listen() this way, since the classes are only known at compile time. I need to store the listeners in a per-producer map, which in turn contains a per-event-name map, something like:</p>
  
  <pre><code>unordered_map&lt;shared_ptr&lt;ProducerClass&gt;,
      unordered_map&lt;string, vector&lt;boost:function1&lt;void, EventClass*&gt; &gt; &gt; &gt; listeners;
  </code></pre>
  
  <p>But of course C++ doesn't let me. I could cheat:</p>
  
  <pre><code>unordered_map&lt;shared_ptr&lt;void*&gt;,
      unordered_map&lt;string, vector&lt;boost:function1&lt;void, void*&gt; &gt; &gt; &gt; listeners;
  </code></pre>
  
  <p>but then that feels terribly dirty.</p>
  
  <p>So now I have to templatize EventManager or something, and keep one for each producer, perhaps? But I don't see how to do that without splitting up the queue, and I can't do that.</p>
  
  <hr>
  
  <p>Note how I am explicitly trying to avoid having to define pure interface classes for each type of event, Java-style:</p>
  
  <pre><code>class CrashEventListener
  {
      virtual void on_crash(CrashEvent* event) = 0;
  }
  </code></pre>
  
  <p>With the number of events I have in mind, that would get awful, fast.</p>
  
  <p>It also raises another issue: I want to have fine-grained control over event handlers. For example, there are many producers that simply provide an event called "change". I want to be able to hook producer A's "change" event to on_a_change, and producer's B "change" event to on_b_change, for example. Per-event interfaces would make that inconvenient at best.</p>
  
  <hr>
  
  <p>With all this in mind, could someone please point me in the right direction?</p>