• Advertisement
Sign in to follow this  

EventBus - Possible with C++ templates?

This topic is 1653 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

 

I guess I'm Java-impaired and want to get back into C++.

Judging from the research I did it might not be possible to accomplish the following in an elegant way ... is that correct?

GWT uses generics in roughly the following way:

 

1.) You define a Handler that extends an abstract Handler class

class PlayerMovedHandler: public EventHandler
{

    ...

public:

    void handlePlayerMovedEvent(PlayerMovedEvent* pEvent)

};

2.) The Event is linked to the Handler type through generics

#ifndef EVENT_H
#define EVENT_H

template <class H>
class AbstractEvent
{
private:

public:

    Event()
    {
    }
    ~Event();

    virtual EventType<H> getEventType();
    virtual void handleEvent(H handler);
};

#endif // EVENT_H

3.) The EventBus must group Handlers by the EventType and iterate over the correct ones in a fireEvent method (in a typesafe way).

And I don't know what the EventBus would look like. How would I store the EventType/EventHandler list pairs?

What would the addHandler(EventType<H>, H) method look like?

 

The idea is, of course, that fireEvent iterates over all the handlers for the event's EventType and passes each handler to the handleEvent method of the event.

 

Is this possible at all ... or are weird casts or tricky C++ features required to pull it off?

Edited by DareDeveloper

Share this post


Link to post
Share on other sites
Advertisement

Thanks for taking the time to write this. I don't want to seem unappreciative, but that is kinda the worst case scenario for me.

I don't really feel comfortable using that as a starting point. Besides, I guess I wouldn't be able to design a game if the generics are that limited (I admit I always struggle with Java as well).

 

The Handler is a function (not a class), or am I completely tripping?

I want to be able to use inheritance to add default behaviour like convenience logging methods to the handlers ... and maybe other things like that.

And modules need to BE EventHandlers (implement the "interface").

 

The whole thing just isn't as expandable as I'd like it to be. And the template trickery bit isn't what I call readable code. I want to understand the code and be able to explain it.

Combined with how impossible it is to compare unicode strings with C++, I guess going back to C++ is not an option ... and at the same time it is the only option.

I just hate my life right now :-(

 

Guess I'll look for different approaches that look nicer and are just a little less elegant.

Share this post


Link to post
Share on other sites

So, the short version is that while Generics in Java or C# seem conceptually similar to C++ templates, they're very different beasts. Thinking in one while programming in the other is a recipe for misery and failure. Its perfectly doable to create a bus for events in C++ of course, you're just not going to accomplish it in the same way you would in Java, and if Java is your standard for what's nice or elegant, then C++ is likely to disappoint you until you understand why it is the way it is. In other words, an elegant C++ solution probably doesn't look like an elegant Java solution, and you probably don't make use of the solution in the same way either.

 

I imagine other things about C++ probably seem distasteful too, like free-standing functions (those not part of a class, or even a namespace), perhaps operator overloading, pointers. Java is a very dogmatic, language-designer-knows-best sort of language, while C and C++ are explicitly opposite. As languages they have different perspectives, and that leads to justifying all the usual trade-offs in different ways.

 

Anyways, I don't mean that to sound snarky, its just a reality of the two languages and where they come from.

Share this post


Link to post
Share on other sites

The Handler is a function (not a class), or am I completely tripping?

Just to be clear: the handler in the code AllEightUp posted is a std::function. That means you can use any callable object, whether it's a namespace-level (free) function, a member function, a lambda expression, or a class with an overloaded operator()(). It's very very expandable.

 

If you just want to use inheritance to implement a Handler interface, there's nothing stopping you, even using AllEightUp's code.  Your base class can just use std::bind to connect the handler to the event.

Share this post


Link to post
Share on other sites
Guest Hiwas

Sorry for the late reply, got a bit busy today.  Anyway, I think the other two replies cover things.  First, thinking derivation is the more flexible solution is unfortunately wrong.  I'm fairly certain that is just Java habits giving you the wrong impression of the intentions here.  Second, if the posted synopsis code was not clear, you really need to sit down with C++ a bit again and catch up.  Using the auto keyword and lamba wrapper is C++11 stuff but it is very simplistic in the way I wrote it.  (in fact, pretty sure the code was more readable than my comments, I typed it fast and didn't proof read. :))  Had I used C++0x or boost, it would have been considerably more complicated because I would have had to use tr1/boost::bind and binding composites are massively ugly and unreadable.  The first time I did something like this used boost and binding composites, internally it was a disaster area for anyone else to look at, but it was all proper and valid usage of the tools available.  Lambda's make things quite a bit more readable.

 

So, before you write this all off, I'd suggest perhaps a bit more relearning of C++ is in order.  :)

Share this post


Link to post
Share on other sites

Well I don't think it is the Java way of doing things that gets in the way.

It is the artist / software architect thinking that tells me that the tool should not dictate the outcome.

That is also what I was told over and over again at the academy that I went to :-(

 

Right now I can't get used to the idea that I can't design first and implement what I designed later.

Having to design FOR C++, that is a real bummer for me. I know what I want to do and then figure out how to do it with the language.

 

I like pointers and I missed operator overloading in Java, but while working with Java / GWT and many cool design patterns I learned to love readable code.

For those reasons I don't even want to understand that:

 

{
  handlerMap.insert(
     id,
     [=]( AbstractEvent* ev )
     {
        cb( *(EventType*)ev;
     }
  );
}

There is a huge DAFUQ going on in my head ... if there is no way to write readable code: I hate my life. The fact that "It could be worse with boost" isn't really helping there ;-)

 

Guess I will look into passing all events that inherit from AbstractEvent to all handlers of an EventBus and use a "bool handleEvent(AbstractEvent)" to check if the handler is responsible for the EventType. Like that I can also write something like an EventLogger as a handler that logs any kind of event (by giving the AbstractEvent a toMessage() function).

But you guys are probably right. I don't think I should go forward at all ... I probably can't get used to C++ any more and would be digging my own grave.

 

 

Its perfectly doable to create a bus for events in C++ of course, you're just not going to accomplish it in the same way you would in Java

I did a google search before posting here and couldn't find anything useful, so right now I doubt that it is even possible. At least with the goals I have in mind.

Do you know any resources or libraries that I could look at for inspiration?

 

 

Just to be clear: the handler in the code AllEightUp posted is a std::function. That means you can use any callable object, whether it's a namespace-level (free) function, a member function, a lambda expression, or a class with an overloaded operator()(). It's very very expandable.

Stupid example, but let's say an Entity is a CollisionEventHandler and I want to add the Entity to the EventBus ...

I suppose I'd have to pass the function of the object to the Register( AbstractEvent::EventTypeId id, std::function< void ( EventType& ) > cb ) function?

... what would the code for something like that look like?

 

Is there a generic way to do it? Let's say Entity is a CollisionEventHandler, an EntityDeletionHandler and an EntityModificationHandler ... is there a way to Register all the handler functions (e.g. with one call to a generic EventBus function that forwards to Register)?

Edited by DareDeveloper

Share this post


Link to post
Share on other sites
Guest Hiwas

Quote
 
{
  handlerMap.insert(
     id,
     [=]( AbstractEvent* ev )
     {
        cb( *(EventType*)ev;
     }
  );
}
There is a huge DAFUQ going on in my head ... if there is no way to write readable code: I hate my life. The fact that "It could be worse with boost" isn't really helping there ;-)

 

Well, it can be rewritten in a more verbose manner which can explain things a bit better and may look a bit cleaner to you.

// Recreate the type we are using to store the "generic" events.
typedef std::function< void ( AbstractEvent* ) > GenericCallback_t;
 
// Create an object, std::function, from a lambda.  The lambda is basically
// just an anonymous function which we can assign to the function object.
GenericCallback_t  trampoline =
  [=]( AbstractEvent* event )
  {
     // Call the type specific handler, casts the abstract event to the proper
     // type and dereferences it to a reference.
     cb( *(EventType*) ev );
  };
 
// Insert into the handler map.  Think this is a "dictionary" in Java?
handlerMap.insert( id, trampoline );

Given that Java does not support lambda's, it is still possible the whole "[](){}" pattern is new to you and doesn't make sense.  Some languages use "=>( params )" (C#) or simple assignment of code (Lua) etc.  The idea behind it though is that it is effectively just syntactic sugar for writing something like the following:

// [](){} replaces all this in a simple manner.
struct Trampoline
{
  // The "capture" portion, i.e. [=] does all this automatically.
  Trampoline( std::function< void( EventType& ) > cb )
  : mCallback( cb )
  {}
 
  // The call parameters portion: i.e. ( AbstractEvent* ).
  template< typename EventType >
  void operator()( AbstractEvent* ae )
  {
     // the actual code portion.
     mCallback( *(EventType*)ae );
  }
 
private:
  // Done by the compiler automatically with the lambda.
  std::function< void( EventType& ) >   mCallback;
};
 
// Bind the struct into a function object.  Probably munched the address of operator () though, always forget the syntax..
auto cb = std::bind( &Trampoline::operator< EventType >(), Trampoline( cb ), std::placeholders::_1 );

All that extra code is handled via the compiler now, which makes the reduction in typing quite considerable and also saves a lot of error prone boiler plate code.  As far as I know, this is how the Boost::Lambda library worked also.  (Well, a bit cleaner.. smile.png)

 

This explanation does not make it read "cleaner" until you get used to the new syntax, but hopefully the explanation helps to make it understandable now until you do get used to it.

Edited by Hiwas

Share this post


Link to post
Share on other sites
Guest Hiwas



Quote
Just to be clear: the handler in the code AllEightUp posted is a std::function. That means you can use any callable object, whether it's a namespace-level (free) function, a member function, a lambda expression, or a class with an overloaded operator()(). It's very very expandable.
Stupid example, but let's say an Entity is a CollisionEventHandler and I want to add the Entity to the EventBus ...
I suppose I'd have to pass the function of the object to the Register( AbstractEvent::EventTypeId id, std::function< void ( EventType& ) > cb ) function?
... what would the code for something like that look like?
 
Is there a generic way to do it? Let's say Entity is a CollisionEventHandler, an EntityDeletionHandler and an EntityModificationHandler ... is there a way to Register all the handler functions (e.g. with one call to a generic EventBus function that forwards to Register)?

 

The first thing here is that using this solution the Entity does not have to "be" a CollisionEventHander, but assuming you want it that way, in the constructor for CollisionEventHandler, or in your derived object, you would write:

 

EventBus::Register< CollisionEventEvent >(
  CollisionEventHandler::getEventTypeId(),  // Get the id from the appropriate super class.
  std::bind( &CollisionEventHandler::handleEvent, this, std::placeholders::_1 ) );  // Bind the function to be called.

Share this post


Link to post
Share on other sites

OP, perhaps we could try it the other way round: could you tell us how would you prefer to *use* the code?

Forget about the design, interfaces, classes *completely* and then think of the ideal "dream code" (as in "dream team") you'd like to have at your disposal at the end -- just thinking out loud, perhaps you're unnecessarily constraining yourself with the particular OOP solution?

 

Next step, consider creating a spike solution (at least one, they're throw-away design explorations anyway).

What are your spike solutions? // See also Spike Described;

Edited by Matt-D

Share this post


Link to post
Share on other sites

{
handlerMap.insert(
   id,
   [=]( AbstractEvent* ev )
   {
      cb( *(EventType*)ev;
   }
   );
}

 

I'd personally format this as:

handlerMap.insert(id, [=](AbstractEvent* ev)
{
	cb( *(EventType*)ev );
});

This might make it clearer for you. Combined with less obscure variable names you might even start liking it smile.png.

Edited by Mussi

Share this post


Link to post
Share on other sites

Im not sure why you need one (there might be a better way to do things) but i assume the 'event' can be any type.

 

Im not going to make a base class for event since one is not needed for this way to do it:

MyEventType //int,string,PlayerMovedEvent....

template<class EventT>
EventHandlerBase
{
typedef EventT EventType;
virtual handleEvent(EventT event);
}

PlayerMovedHandler : public EventHandlerBase<PlayerMovedEvent>
{
void handleEvent(PlayerMovedEvent& event){blehblah}
}

EventBus
{
map<size_t, list<void*>> handlers; //maps hashcode of event type to list of handlers of that event type

template<class EventT>
fire(EventT& event)
{
    for each in handlers.get(typeid(EventT).hash_code()) do
    {
        reinterpret_cast<EventHandlerBase<EventT>*>(handler).handleEvent(event); //ew
    }
}

template<class HandlerT>
insertHandler(HandlerT& handler)
{
    map.get(typeid(HandlerT::EventType).hash_code()).push_front(&handler);
}
}

usage:

 

EventBus bus;

bus.insertHandler(PlayerMovedHandler);

bus.fire(PlayerMovedEvent()); //calls all handlers with EventT = PlayerMovedEvent

 

This groups the handlers by the hash code of the events type. It assumes you know the type of event when firing one and when creating handlers.

The run time cost is the virtual call + looking up the hash code from the map (use std::unordered_map for faster lookup times)

The typeid/hash_code stuff is probably done at compile time so it shouldnt matter.

Share this post


Link to post
Share on other sites

Would love to answer all your questions ... but I can't think straight right now.

Can't wrap my head around static polymorphism and runtime polymorphism.

 

Basically what I want to do is avoid adding all modules to all modules. I want an event bus to couple them more loosely.

 

I still don't see how a function can be more powerful than a class.

The construct I posted (where the event knows the handler and determines how the handler is used) would make so many different things possible.

Don't really want to name a few ... it is a general thing. I don't even know what I will want to do with it in the future so it would just be a waste of time if you'd post solutions.

Guess once I understand the problem I have on a more meta level I'll post again.

 

I think I will run into similar problems once I want to write a property mechanism that turns strings into other types.

 

I tried to write a solution with an EventManager that uses one EventBus for each event type ... but I wanted polymorphism in there as well

#ifndef EVENTHANDLER_H
#define EVENTHANDLER_H

#include "types.h"

#include "logger.h"

template <class E>
class EventHandler
{
private:

    std::string m_name;

public:

    EventHandler(std::string name);
    virtual ~EventHandler();

    virtual void handleEvent(E* pEvent)
    {
        Logger* pLogger = new Logger("otherlogger.html", Logger::LOGLEVEL_INFO);
        pLogger->log("handleEvent of EventHandler called");
        delete(pLogger);
        pLogger = NULL;
    }

    std::string getName()
    {
        return m_name;
    }
};

template <class E>
EventHandler<E>::EventHandler(std::string name)
    : m_name(name)
{
}

template <class E>
EventHandler<E>::~EventHandler()
{
}

#endif // EVENTHANDLER_H

...

#ifndef EVENTBUS_H
#define EVENTBUS_H

#include "types.h"

#include "eventhandler.h"
#include "logger.h"
#include "profiler.h"

template <class E>
class EventBus
{
private:

    Logger* m_pLogger;
    Profiler* m_pProfiler;

    std::list< EventHandler<E> > m_handlerList;

public:

    EventBus()
        : m_pLogger(NULL), m_pProfiler(NULL)
    {
    }
    ~EventBus()
    {
    }

    EventBus<E>* initialize(Logger* pLogger, Profiler* pProfiler);
    EventBus<E>* uninitialize();
    EventBus<E>* reset();

    EventBus<E>* fireEvent(E* pEvent);
    EventBus<E>* addEventHandler(EventHandler<E> eventHandler);
    EventBus<E>* removeEventHandler(EventHandler<E> eventHandler);
};

template <class E>
EventBus<E>* EventBus<E>::initialize(Logger* pLogger, Profiler* pProfiler)
{
    m_pLogger = pLogger;
    m_pProfiler = pProfiler;

    return this;
}

template <class E>
EventBus<E>* EventBus<E>::uninitialize()
{
    m_pProfiler = NULL;
    m_pLogger = NULL;

    return this;
}

template <class E>
EventBus<E>* EventBus<E>::reset()
{
    m_handlerList.clear();
    return this;
}

template <class E>
EventBus<E>* EventBus<E>::fireEvent(E* pEvent)
{
    Uint32 i = 1;
    Message* pMessage = new Message();
    for (typename std::list< EventHandler<E> >::iterator it = m_handlerList.begin(); m_handlerList.end() != it ; ++it)
    {
        EventHandler<E> handler = *it;

        *pMessage << "Informing handler nr. " << i << ": " << handler.getName().c_str();
        m_pLogger->log(pMessage, Logger::LOGLEVEL_INFO);

        handler.handleEvent(pEvent);

        i++;
        pMessage->reset();
    }
    delete(pMessage);
    pMessage = NULL;

    return this;
}

template <class E>
EventBus<E>* EventBus<E>::addEventHandler(EventHandler<E> eventHandler)
{
    m_handlerList.push_back(eventHandler);
    return this;
}

template <class E>
EventBus<E>* EventBus<E>::removeEventHandler(EventHandler<E> eventHandler)
{
    for (typename std::list< EventHandler<E> >::iterator it = m_handlerList.begin(); it != m_handlerList.end(); ++it)
    {
        if (&eventHandler == &(*it))
        {
            m_handlerList.erase(it);
            break;
        }
    }

    return this;
}

#endif // EVENTBUS_H

... obviously there I can't overwrite the handleEvent(E*) in a specialized handler with a method like handleEvent(EventInput*).

 

I just can't really think of other ways to do something similar.

 

 

But thanks everybody, I guess I'll just start implementing the above solution and see if I can get used to working with it.

Edited by DareDeveloper

Share this post


Link to post
Share on other sites

(0) Would love to answer all your questions ... but I can't think straight right now.

(1) Can't wrap my head around static polymorphism and runtime polymorphism.

 

Basically what I want to do is avoid adding all modules to all modules. I want an event bus to couple them more loosely.

 

(2) I still don't see how a function can be more powerful than a class.

 

(0) I guess trying out a couple of alternative, throwaway but concrete (compilable) solutions might be your best course of action, then.

 

(1) Go through this and try out the examples, that should do it: http://accu.org/index.php/journals/538

 

Note that static forms of polymorphism (both parametric/templates and overloading) are somewhat more powerful in C++ than dynamic polymorphism (in form of inclusion polymorphism/inheritance), since they support multiple dispatch out-of-the-box (so there's no need for things like the visitor pattern in Java if you're fine with the compile-time world), see: http://www.codeproject.com/Articles/635264/Open-Multi-Methods-for-Cplusplus11-Part-1

To see how this comes up in game development, consider the asteroids-spaceships collisions example:

https://en.wikipedia.org/wiki/Multiple_dispatch#Examples

// Note that if you need dynamic/run-time polymorphism, you will also need to use something like the visitor pattern (or a library-based solution), even in C++.

 

(2) Things like std::function support type-erasure and directly support catamorphisms (again, completely eliminate the need for design patterns like the visitor pattern), so a lot of the things you'd normally need a lot of OOP boilerplate for (to implement the design patterns) are simply supported out of the box (no manually written boilerplate code) if you write in FP style:

http://lorgonblog.wordpress.com/2008/04/09/catamorphisms-part-three/

http://stackoverflow.com/questions/2527153/are-design-patterns-specific-to-language-or-technology

 

In particular, in OOP style you often have to waste your time coding some brittle/rigid boilerplate requiring inheritance, while in FP style with std::function you can completely decouple many of your components. Here's another example in the gamedev context: http://probablydance.com/2012/12/16/the-importance-of-stdfunction/

 

Consider the traditional (OOP/inheritance solution):

But you’ve got that inheritance in there, and that doesn’t scale. You will probably want to add a render loop and maybe a separate update loop for the editor. Once you’ve got that many base classes it makes sense to combine them into one. And now you’ve just started on the way of having a big base class that gets used everywhere. Soon enough adding something different to the update loop involves a whole lot of unnecessary work.

 

And compare with the FP one:
 

This has no inheritance. Meaning I can add anything to the update loop now. If I create a new object that is unrelated to anything I had before and is managed completely differently, I can still add it just as easily as anything else. And I can use the same idea for a render loop or for implementing an observer pattern. It makes everything immensely easier.

Perhaps this is exactly what you're looking for?

 

Just as with anything in programming, there ain't no such thing as a free lunch, so each solution has upsides and downsides -- it's your wonderful job as a designer to compare and contrast each and chose the least-imperfect one for the task at hand :-)

 

In particular, both std::function and OOP/inheritance will have a run-time cost related to type-erasure/virtual function call; in contrast, if it's possible for you to know the types at compile-time and you can design & code a GP solution with templates, you won't suffer any overhead of this type (although may have icache trade-offs to consider, etc.).

Edited by Matt-D

Share this post


Link to post
Share on other sites

Thanks ... trying to move towards clarity by playing around with more basic things. Can anybody explain why the following doesn't work?

I thought that should be a very trivial case of inheritance.

#ifndef EVENTHANDLER_H
#define EVENTHANDLER_H

#include "types.h"

#include "busevent.h"
#include "logger.h"

class EventHandler
{
private:

    std::string m_name;

public:

    EventHandler(std::string name);
    virtual ~EventHandler();

    virtual void handleEvent(BusEvent* pEvent)
    {
    }

    std::string getName()
    {
        return m_name;
    }
};

#endif // EVENTHANDLER_H

I have event handlers and they only handle BusEvent pointers ...

 

The engine is one of those event handlers ...

#ifndef ENGINE_H
#define ENGINE_H

#include "types.h"

#include "logger.h"
#include "module.h"
#include "profiler.h"
#include "eventmanager.h"

class Engine: public EventHandler
{

private:

    bool m_isRunning;

    Logger* m_pLogger;
    Profiler* m_pProfiler;
    EventManager* m_pEventManager;

    std::list<Module*> m_pModuleList;

public:

    Engine();
    virtual ~Engine();

    Engine* initialize(Logger* pLogger, Profiler* pProfiler, EventManager* pEventManager);
    Engine* uninitialize();

    Engine* registerModule(Module* pModule);

    bool isRunning()
    {
        return m_isRunning;
    }

    Engine* start();

    Engine* beginFrame();
    Engine* doFrame();
    Engine* endFrame();

    void handleEvent(BusEvent* pEvent);
};

#endif // ENGINE_H

the event bus calls handleEvent using the engine pointer (the logfile says it is the engine) ... but it seems that the handleEvent of EventHandler is called, not Engine.

Why is that?

 

Here is the EventBus fireEvent(...) code ...

EventBus* EventBus::fireEvent(BusEvent* pEvent)
{
    Uint32 i = 1;
    Message* pMessage = new Message();
    for (typename std::list< EventHandler >::iterator it = m_handlerList.begin(); m_handlerList.end() != it ; ++it)
    {
        EventHandler handler = *it;

        *pMessage << "Informing handler nr. " << i << ": " << handler.getName().c_str();
        m_pLogger->log(pMessage, Logger::LOGLEVEL_DEBUG);

        handler.handleEvent(pEvent);

        i++;
        pMessage->reset();
    }
    delete(pMessage);
    pMessage = NULL;

    return this;
}

... and handleEvent(...) of the engine:

void Engine::handleEvent(BusEvent* pEvent)
{
    m_pLogger->log("Handling event ...", Logger::LOGLEVEL_INFO);
    if (BusEvent::TYPE_QUIT == pEvent->getEventType())
    {
        m_isRunning = false;
    }
    else
    {
        Message* pMessage = new Message();
        *pMessage << "event unknown: " << pEvent->getEventType();
        m_pLogger->log(pMessage, Logger::LOGLEVEL_INFO);
        delete(pMessage);
        pMessage = NULL;
    }

    EventHandler::handleEvent(pEvent);
}

Doesn't virtual mean that it shouldn't matter that the EventBus is working with an EventHandler pointer?

Share this post


Link to post
Share on other sites

the event bus calls handleEvent using the engine pointer (the logfile says it is the engine) ... but it seems that the handleEvent of EventHandler is called, not Engine.

Why is that?

 

Doesn't virtual mean that it shouldn't matter that the EventBus is working with an EventHandler pointer?

 

Only if you store & call it via a pointer (or its variants, like std::unique_ptr) or a reference (or its variants, like std::reference_wrapper).

Otherwise, object slicing occurs.

 

Just like here, where you store by value:

std::list< EventHandler<E> > m_handlerList; // oops

 

and here, where you call your stored-by-value (and sliced) handler

for (typename std::list< EventHandler >::iterator it = m_handlerList.begin(); m_handlerList.end() != it ; ++it) // oops
{
EventHandler handler = *it; // oops

*pMessage << "Informing handler nr. " << i << ": " << handler.getName().c_str();
m_pLogger->log(pMessage, Logger::LOGLEVEL_DEBUG);
handler.handleEvent(pEvent); // this is sliced

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement