Jump to content
  • Advertisement
Sign in to follow this  
DareDeveloper

EventBus - Possible with C++ templates?

This topic is 1896 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
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!