{"id":191,"date":"2010-06-18T20:23:01","date_gmt":"2010-06-18T20:23:01","guid":{"rendered":"http:\/\/grandmaster.student.utwente.nl\/?page_id=191"},"modified":"2017-07-29T18:48:34","modified_gmt":"2017-07-29T17:48:34","slug":"overdrive-assault-part-3","status":"publish","type":"page","link":"https:\/\/www.grandmaster.nu\/blog\/?page_id=191","title":{"rendered":"Game Engine part 3"},"content":{"rendered":"<p>In this tutorial I will share an approach that allows for event-response like programming in C++. The technique uses both global statics and a couple of layers of indirection, so it may not be the most elegant of implementations, but it does allow for very pretty code. A sample:<\/p>\n<pre lang=\"c++\">...(some includes)...\r\nstruct MyEvent {\r\n  int value;\r\n};\r\n\r\nstruct TriggerHandler {\r\n  void operator()(const MyEvent& evt) {\r\n     std::cout << \"Event received: \" << evt.value << \"\\n\";\r\n  }\r\n};\r\n\r\nint main() {\r\n  TriggerHandler handler;\r\n  Channel.add<MyEvent>(&handler);\r\n  MyEvent aaa { 123 };\r\n  Channel.broadcast(aaa); \/\/ transmit object\r\n  Channel.broadcast(MyEvent { 456 }); \/\/ pass anonymous object\r\n}\r\n<\/pre>\n<p>Which outputs exactly what you would expect. Notice however, the lack of inheritance and the absence of strict coupling between the objects. Many game engines I&#8217;ve seen implement some type of global messaging system, but usually they either use a signal-slot approach which requires pretty serious coupling of the components, or a base message with destination flags are used. In the latter case, all objects are considered as possible recipients of a signal, not just the objects that can actually handle the message in question.<\/p>\n<p>The system I&#8217;ll describe here creates a separate queue at compile time for each separate type of message that can be transmitted. This encourages using small or even empty structs as a message, but really any message will do because this is template-based instead of inheritance. Even though it is template based, when creating an infrastructure system like this it&#8217;s preferred to start out with a concrete type and then generalize it using templates. So we start out with defining a class Channel that contains a list of handlers for the type MyEvent:<\/p>\n<pre lang=\"c++\">class Channel {\r\npublic:\r\n  void add(void* handler) { \/\/ the actual handler type could be anything, but still needs to be an object\r\n    mHandlers.push_back(handler);\r\n  }\r\n\r\n  void broadcast(const MyEvent& evt) {\r\n    for (auto& handler: mHandlers)\r\n      (*handler)(evt);  \/\/ this won't compile, because the stored type can't be dereferenced :(\r\n  }\r\n\r\nprivate:\r\n  std::vector<void*> mHandlers;\r\n};<\/pre>\n<p>Immediately, this presents an issue &#8212; accepting any type won&#8217;t allow the desired function to be called. There are a couple of ways around this, but it is more difficult than one might think. One solution, which I prefer is to capture the handler in a lambda, and then store it in a std::function as follows:<\/p>\n<pre lang=\"c++\">class Channel {\r\npublic:\r\n  template <typename T>\r\n  void add(T* handler) {\r\n    mHandlers.push_back(\r\n      [handler] (const MyEvent& evt) {\r\n        (*handler)(evt);\r\n      }\r\n    );\r\n  }\r\n\r\n  void broadcast(const MyEvent& evt) {\r\n    for (auto& handler: mHandlers)\r\n      handler(evt); \/\/ ok now, each handler has an appropriate operator()\r\n  }\r\n\r\nprivate:\r\n  std::vector<std::function<void(const MyEvent&#038;)>> mHandlers;\r\n};<\/pre>\n<p>The lambda&#8217;s stored capture the original pointer at compile time, preserving the original type. But the lambdas themselves are stored using std::function, which uses type erasure. By using this style, already all objects that have an operator() for MyEvent will work now. Generalizing to all message types is just one template away:<\/p>\n<pre lang=\"c++\">template <typename tMessage>\r\nclass Channel {\r\npublic:\r\n  template <typename tHandler>\r\n  void add(tHandler* handler) {\r\n    mHandlers.push_back([handler] (const tMessage& msg) { (*handler)(msg); });\r\n  }\r\n\r\n  void broadcast(const tMessage& msg) {\r\n    for (auto& handler: mHandlers)\r\n      handler(msg);\r\n  }\r\n};\r\n<\/pre>\n<p>While the channel is very generic now, there is still the issue of coupling, for which I use static instances of the Channel for each message type used. This way, exactly those objects that want to respond to a message type will be reached, with the only dependency between the objects being the message communicated:<\/p>\n<pre lang=\"c++\">\r\nclass Channel {\r\npublic:\r\n  template <typename tMessage, typename tHandler>\r\n  static void add(tHandler* handler) { \r\n    \/\/ typically, the handler type is derived while the message type would be explicit\r\n    \/\/ e.g. Channel<MyEvent>::add(this);\r\n    InternalChannel<tMessage>::instance().add(handler); \/\/forward to the appropriate queue\r\n  }\r\n\r\n  template <typename tMessage>\r\n  static void broadcast(const tMessage& message) { \r\n    \/\/ usually no need to be explicit, the message type can be derived at compiletime\r\n    InternalChannel<tMessage>::instance().broadcast(message);\r\n  }\r\n\r\nprivate:\r\n  template <typename tMessage>\r\n  class InternalChannel {\r\n  public:\r\n    static InternalChannel& instance() {\r\n      static InternalChannel result;\r\n      return result; \/\/ return a reference to an internal static\r\n    }\r\n\r\n    template <typename tHandler>\r\n    void add(tHandler* handler) {\r\n      mHandlers.push_back([handler](const tMessage& msg) { (*handler)(msg); });\r\n    }\r\n\r\n    void broadcast(const tMessage& msg) {\r\n      for (auto& handler: mHandlers)\r\n        handler(msg);\r\n    }\r\n\r\n  private:\r\n    InternalChannel() {} \/\/ private constructor -- only access through static interface\r\n\r\n    std::vector<std::function<void(const tMessage&#038;)>> mHandlers;\r\n  };\r\n};\r\n<\/pre>\n<p>Now the snippet will actually perform as we&#8217;d hope and we can adress the fact that, while there is an &#8216;add&#8217; there is no &#8216;remove&#8217; in this class. Because the actual stored object can no longer be compared to the originally submitted pointer we&#8217;re in a bit of a bind. The solution I&#8217;ve come up with is to just go and store the original pointer in a list that is kept in sync with the handler list. There are some disadvantages to this, as long as we&#8217;re keeping this class &#8216;straightforward&#8217; they should be manageable at least.<\/p>\n<pre lang=\"c++>\r\n...(InternalChannel)...\r\n    template <typename tHandler>\r\n    void add(tHandler* handler) {\r\n      mHandlers.push_back([handler](const tMessage& msg) { (*handler)(msg); });\r\n      mOriginalPtrs.push_back(handler);\r\n      assert(mHandlers.size() == mOriginalPtrs.size());\r\n    }\r\n\r\n    template <typename tHandler>\r\n    void remove(tHandler* handler) {\r\n      auto it = std::find(mOriginalPtrs.begin(), mOriginaPtrs.end(), handler);\r\n      if (it == mOriginalPtrs.end())\r\n        throw std::runtime_error(\"Tried to remove a handler that was not in the handler list\");\r\n\r\n      auto idx = (it - mOriginalPtrs.begin()); \/\/ convert iterator to index\r\n\r\n      mHandlers.erase(mHandlers.begin() + idx);\r\n      mOriginalPtrs.erase(it);\r\n    }\r\n    \r\n  private:\r\n    typedef std::function<void(const tMessage&#038;)> Handler;\r\n    std::vector<Handler> mHandlers;\r\n    std::vector<void*> mOriginalPtrs;\r\n<\/pre>\n<p>Pretty nice, if I do say so myself. However, this does introduce the possibility that a handler would unregister itself upon handling a message. Doing this will invalidate the iterator in the broadcast function, so that needs a little patch:<\/p>\n<pre lang=\"c++\">...(InternalChannel)...\r\n    void broadcast(const tMessage& msg) {\r\n       auto localQueue = mHandlers; \/\/ create a local copy\r\n\r\n       for (auto& handler: localQueue)\r\n         handler(msg);\r\n    }\r\n<\/pre>\n<p>Because I plan to use this heavily to communicate between threads, it needs to be threadsafe. The simplest way to do this is to just introduce a mutex that guards access to the handler list. All message types are separate queues anyway, so actual blocking should be reduced to a minimum.<\/p>\n<pre lang=\"c++\">...(InternalChannel)....\r\n    template <typename tHandler>\r\n    void add(tHandler* handler) {\r\n      std::lock_guard<std::mutex> lock(mMutex);\r\n      mHandlers.push_back([handler](const tMessage& msg) { (*handler)(msg); });\r\n      mOriginalPtrs.push_back(handler);\r\n      assert(mHandlers.size() == mOriginalPtrs.size());\r\n    }\r\n\r\n    template <typename tHandler>\r\n    void remove(tHandler* handler) {\r\n      std::lock_guard<std::mutex> lock(mMutex);\r\n      auto it = std::find(mOriginalPtrs.begin(), mOriginaPtrs.end(), handler);\r\n      if (it == mOriginalPtrs.end())\r\n        throw std::runtime_error(\"Tried to remove a handler that was not in the handler list\");\r\n\r\n      auto idx = (it - mOriginalPtrs.begin()); \/\/ convert iterator to index\r\n\r\n      mHandlers.erase(mHandlers.begin() + idx);\r\n      mOriginalPtrs.erase(it);\r\n    }\r\n\r\n    void broadcast(const tMessage& msg) {\r\n      std::vector<Handler> localQueue(mHandlers.size()); \/\/ don't copy just yet, but allocate enough space\r\n      { \/\/ lock for as little time as possible\r\n        std::lock_guard<std::mutex> lock(mMutex);\r\n        localQueue = mHandlers; \/\/ *now* copy\r\n      }\r\n\r\n      for (auto& handler: localQueue)\r\n         handler(msg);\r\n    }\r\n...\r\n<\/pre>\n<p>This allows message structs to be communicated between threads, but take heed of the fact that broadcasting a message occurs fully in the thread that is doing the broadcast call. There is no buffering or message queue at the receiving end or anything. One of the strengths of this approach is that the message type does not have to be copyable as only const references are passed to the receiving objects. The main disadvantage is that it uses global objects with mutexes and two layers of indirection, so it may not be quite optimal performance-wise.<\/p>\n<p>Interestingly the design suggests not quite a base class but more of a contract that an object should fulfill to be considered a valid message handler. The contract can be implemented as an abstract base class (but it&#8217;s not required):<\/p>\n<pre lang=\"c++\">template <typename tMessage>\r\nclass MessageHandler {\r\npublic:\r\n  MessageHandler() {\r\n    Channel::add<tMessage>(this);\r\n  }\r\n\r\n  virtual ~MessageHandler() {\r\n    Channel::remove<tMessage>(this);\r\n  }\r\n\r\n  virtual void operator()(const tMessage&) = 0;\r\n};\r\n<\/pre>\n<p>The complete code can be downloaded <a href=\"http:\/\/www.grandmaster.nu\/custom\/channel.zip\">here<\/a>.<\/p>\n<p>I think I&#8217;ll leave it at that for this tutorial. As usual, let me know if there are any bugs and\/or suggestions with the presented code \ud83d\ude42 <\/p>\n<p>Until next time!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial I will share an approach that allows for event-response like programming in C++. The technique uses both global statics and a couple of layers of indirection, so it may not be the most elegant of implementations, but it does allow for very pretty code. A sample: &#8230;(some <a class=\"more-link\" href=\"https:\/\/www.grandmaster.nu\/blog\/?page_id=191\">Read More&#8230;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":25,"menu_order":0,"comment_status":"open","ping_status":"open","template":"","meta":{"ngg_post_thumbnail":0,"footnotes":""},"_links":{"self":[{"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=\/wp\/v2\/pages\/191"}],"collection":[{"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=191"}],"version-history":[{"count":15,"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=\/wp\/v2\/pages\/191\/revisions"}],"predecessor-version":[{"id":1046,"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=\/wp\/v2\/pages\/191\/revisions\/1046"}],"up":[{"embeddable":true,"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=\/wp\/v2\/pages\/25"}],"wp:attachment":[{"href":"https:\/\/www.grandmaster.nu\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=191"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}