Sign in to follow this  
sjaakiejj

Events, EventManagement and EventListeners - Suggestions?

Recommended Posts

sjaakiejj    130
Hi,

I've been wondering about this for around a week now, and I'm still not quite sure how to solve it. I come up with different ideas every once in a while, but none of them seem to be "The Best" way of doing it, and some have downright ugly disadvantages.

So here's my question. I've got an Event Management System that allows EventListeners to register with the EventManager, and that allows any class in the system to dispatch an Event. These events can be of an arbitrary number of different types, including but not limited to Movement Events (by both the Player and AI), State Change Events, Game Exit Events and Interaction Events. All of these events need their own specific body, for instance, a Movement Event might need a source (The object that does the moving) and a direction vector. A State Change Event needs to know about the next state. Game Exit Event doesn't need any information, and an Interaction event needs a source and a destination object.

This diversity forms a problem for me, as the first structure I thought of was having an abstract class "Event" and an abstract class "EventListener". EventListener has a method called "Notify" and "handleEvents", and any class that needs to be an event listener can simply inherit those methods. Any Event will just be a subclass of the "Event" class. Having simply one notify method would mean that Casting has to be used, as far as I can see, and I want to avoid that.

A solution to casting I thought of is to have many different "Notify" methods, one for each event. Each of these would have an implementation in the EventListener interface (though the implementation would be to just do nothing with the event), and inheriting classes would simply override the one they're interested in. This would leave the interface rather bloated, with a lot of code, and not a lot of flexibility.

Other options I came across were void pointers, which again imply casting; Further abstracting the Event objects, making a more defined structure and being able to split the events at a higher level, but this would make notification more complicated due to code duplication.

I'd love to hear how you guys approach this problem, as I'm running out of ideas. I'm not looking for a "Quick Hack" as you might've already figured out, as I want the fundamental design to be solid and high quality for this project.

Share this post


Link to post
Share on other sites
Hodgman    51223
Do you [i]really[/i] need a general event system in the first place? A regular function call [i]is an event[/i].

If you do need to arbitrarily plug callbacks into each other in a completely ad-hoc manner though, check out boost::function, it will help a lot.

Share this post


Link to post
Share on other sites
GorbGorb    112
If your compiler supports variadic templates and you don't care about recompiling when adding/changing events you could use something like this:

[code]

template< class ...MessageTypes >
class MessageListener;

template<>
class MessageListener<>
{
};
template< class Head , class ...Tail >
class MessageListener< Head , Tail... >
: public MessageListener< Tail... >
{
public:
virtual void listen( const Head &Message )
{
}
};


[/code]

Then you register messages:

[code]
struct MessageA
{
//....
};
struct MessageB
{
//...
};
//I don't think MessageA and MessageB have to be complete types... not completely sure though, try it
//(if so, just forward declare and include a definition where you need it to avoid recompiling)

typedef MessageListener< MessageA , MessageB > Listener;

[/code]


and use it like this:

[code]

class SomeSpecificListener
: public Listener
{
public:
void listen( const MessageA & ) //override
{
//...
}
};

int main()
{
SomeSpecificListener special_listener;
Listener &base = special_listener;
base.listen( MessageA() ); //calls your override
base.listen( MessageB() ); //doesn't do anything
}

[/code]

Share this post


Link to post
Share on other sites
speciesUnknown    527
For simplicity, I recommend you avoid the more complex implementations, and simply go with a system of void pointers, a bunch of different structs for each message, and an enum for the type of message. This method is fast, simple, and robust, and you can handle any dynamic dispatch manually using a switch statement.

For many years I messed about with different message systems, but in the end, I settled for this.

Share this post


Link to post
Share on other sites
Trefall    340
Have you looked into signal/slot and delegates?
[url="http://sourceforge.net/projects/sigslot/"]http://sourceforge.net/projects/sigslot/[/url]
[url="http://www.codeproject.com/KB/cpp/FastDelegate.aspx"]http://www.codeproject.com/KB/cpp/FastDelegate.aspx[/url]

Share this post


Link to post
Share on other sites
Frank Taylor    191
[quote]For simplicity, I recommend you avoid the more complex implementations, and simply go with a system of void pointers, a bunch of different structs for each message, and an enum for the type of message. This method is fast, simple, and robust, and you can handle any dynamic dispatch manually using a switch statement.

For many years I messed about with different message systems, but in the end, I settled for this. [/quote]Do you have any code/pseudo-code?

Share this post


Link to post
Share on other sites
GorbGorb    112
I think he means something like


[code]
class Listener
{
public:
virtual void listen( unsigned int MessageID , void *AdditionalInformation )
{
}
//or pure virtual
};

struct MessageA
{
const static unsigned int ID = 0; //or put IDs in a seperate enum
//no data
};
struct MessageB
{
const static unsigned int ID = 1;
char data[10];
};

class SomeListener
: public Listener
{
public:
void listen( unsigned int ID , void *AdditionalInformation )
{
switch( ID )
{
case MessageA::ID:
//....
break;
case MessageB::ID:
MessageB &message = *reinterpret_cast< MessageB* >( AdditionalInformation );
//.....
break;
}
};
};


int main()
{
SomeListener derived;
Listener &base = derived;
base.listen( MessageA::ID , &MessageA() );
MessageB message_b;
//set data
base.listen( MessageB::ID , &message_b );
}

[/code]

I don't see how this approach is simpler then the one I showed, it definitely has more boilerplate than my example.
I may be wrong, but I think the template solution is faster:

template version overhead:
- virtual function call with 1 parameter: constant time

cast - version:
- virtual function call with 2 parameters: constant time
- switch statement: depending on compiler optimizations either constant time (jump table) , logarithmic (binary search) or linear (naive search)

However, since the compiler has to create a vtable with entries for each message for each specific listener, the template version consumes probably more memory, increasing with both the number of your messages and the number of your listeners (classes).

Share this post


Link to post
Share on other sites
sjaakiejj    130
First of all, thanks for all the responses, I'll have a look at all the given suggestions.
[quote name='speciesUnknown' timestamp='1312947911' post='4847013']
For simplicity, I recommend you avoid the more complex implementations, and simply go with a system of void pointers, a bunch of different structs for each message, and an enum for the type of message. This method is fast, simple, and robust, and you can handle any dynamic dispatch manually using a switch statement.

For many years I messed about with different message systems, but in the end, I settled for this.
[/quote]


In fact, I'm trying a system at the moment which is similar to this. I have it structured as such:


[code]

class Message{
public:
enum Subject{
MOVEMENTEVENT,
INTERACTIONEVENT,
STATECHANGEEVENT,
STATEPAUSEEVENT,
STATEPUSHEVENT,
GAME_EXIT_EVENT
};

struct Body{
std::string name;
union Object{
GameObject* object;
Vector3 vector;
State* state;
};
};


public:
Message(Subject lSubject, std::list<Body>* lBody = 0);
~Message();

virtual Subject getSubject();
virtual std::list<Body>* getBody();

private:
std::list<Body>* body;
Subject subject;
};
[/code]

Then in the code itself I simply run a switch statement, checking if this particular listener got any messages. It's rather similar to a Map based approach, as well as the approach you mentioned. The plus-side for me is that I don't need casting, the downside is how many lines it takes to create a message. I haven't really looked into performance yet, but so far it seems to work. What do you guys feel about this?

Thanks again for your time and responses!

Edit:
GorbGorb, I really like your approach, though I really need to take some time and figure out why it works. I'm not familiar with the coding approach you've taken, so any additional explanation would be great!

Share this post


Link to post
Share on other sites
GorbGorb    112
Are you familiar with templates? If not, you should probably first look into a tutorial about them, and afterwards something about variadic templates (c++0x feature).
If you don't want to spend many hours trying to understand them (well, it took me that long... maybe you learn faster),
you should probably just use the void pointer solution.
To your code: It feels a little bit bloated to me. Think of how to receive a message:
- check subject with getSubject()
- iterate over the std::list from getBody()
- check which actual data is in an element with something like "if( name == "..." ) "
- access the actual data with something like "iterator->object.object"
(I assume you want an instance of that union in your struct?)
- probably even more ifs and whiles for actually processing the message

I don't think you'll be happy with that kind of hard to understand code.
suggestions:
- why do you need a std::list with bodies for each event?
- why do you store the type of a body in a string? Enums are much faster and just as easy to read (maybe even better)

PS: Are you interested in the first or the second code snippet I posted?

Share this post


Link to post
Share on other sites
speciesUnknown    527
[quote name='GorbGorb' timestamp='1312972533' post='4847107']
I think he means something like


[code]
class Listener
{
public:
virtual void listen( unsigned int MessageID , void *AdditionalInformation )
{
}
//or pure virtual
};

struct MessageA
{
const static unsigned int ID = 0; //or put IDs in a seperate enum
//no data
};
struct MessageB
{
const static unsigned int ID = 1;
char data[10];
};

class SomeListener
: public Listener
{
public:
void listen( unsigned int ID , void *AdditionalInformation )
{
switch( ID )
{
case MessageA::ID:
//....
break;
case MessageB::ID:
MessageB &message = *reinterpret_cast< MessageB* >( AdditionalInformation );
//.....
break;
}
};
};


int main()
{
SomeListener derived;
Listener &base = derived;
base.listen( MessageA::ID , &MessageA() );
MessageB message_b;
//set data
base.listen( MessageB::ID , &message_b );
}

[/code]

I don't see how this approach is simpler then the one I showed, it definitely has more boilerplate than my example.
I may be wrong, but I think the template solution is faster:

template version overhead:
- virtual function call with 1 parameter: constant time

cast - version:
- virtual function call with 2 parameters: constant time
- switch statement: depending on compiler optimizations either constant time (jump table) , logarithmic (binary search) or linear (naive search)

However, since the compiler has to create a vtable with entries for each message for each specific listener, the template version consumes probably more memory, increasing with both the number of your messages and the number of your listeners (classes).
[/quote]

Your example pretty much demonstrates exactly what I would do. Template based message filtering is one of the things I tried but again, for [b]simplicity[/b] i recommend the message ID + casting method. The amount of boilerplate required to handle messages in a simple scenario seems to be increased, but as your number of message types increases, your template based version will use huge amounts of memory and be considerably complicated. I'm not sure how you would implement decorators, relays, or other types of message handlers with the templated version either - would it require you to do something like,
MessageHandler<MessageA, MessageB, MessageC, MessageD, MessageE, MessageF, MessageG, MessageH, MessageI, MessageJ, MessageK, MessageL>
[img]http://public.gamedev.net/public/style_emoticons/default/huh.gif[/img]

Share this post


Link to post
Share on other sites
sjaakiejj    130
[quote name='GorbGorb' timestamp='1312981145' post='4847145']
Are you familiar with templates? If not, you should probably first look into a tutorial about them, and afterwards something about variadic templates (c++0x feature).
If you don't want to spend many hours trying to understand them (well, it took me that long... maybe you learn faster),
you should probably just use the void pointer solution.
To your code: It feels a little bit bloated to me. Think of how to receive a message:
- check subject with getSubject()
- iterate over the std::list from getBody()
- check which actual data is in an element with something like "if( name == "..." ) "
- access the actual data with something like "iterator->object.object"
(I assume you want an instance of that union in your struct?)
- probably even more ifs and whiles for actually processing the message

I don't think you'll be happy with that kind of hard to understand code.
suggestions:
- why do you need a std::list with bodies for each event?
- why do you store the type of a body in a string? Enums are much faster and just as easy to read (maybe even better)

PS: Are you interested in the first or the second code snippet I posted?

[/quote]

I'm definitely familiar with templates. From your comment on my code, that's indeed something I thought about, and the main issue for me would've been to somehow enforce a certain order in the list. I'm interested in the first snippet you posted, which I'm trying to understand but I have no real experience with variadic templates, so I'll try to read up on that.

I'm not sure if my compiler supports it though, and if it doesn't I'll probably go with an easier solution such as the one suggested by speciesUnknown, and detailed by you.

Share this post


Link to post
Share on other sites
GorbGorb    112
[quote name='speciesUnknown' timestamp='1312983628' post='4847151']
Your example pretty much demonstrates exactly what I would do. Template based message filtering is one of the things I tried but again, for [b]simplicity[/b] i recommend the message ID + casting method. The amount of boilerplate required to handle messages in a simple scenario seems to be increased, but as your number of message types increases, your template based version will use huge amounts of memory and be considerably complicated. I'm not sure how you would implement decorators, relays, or other types of message handlers with the templated version either - would it require you to do something like,
MessageHandler<MessageA, MessageB, MessageC, MessageD, MessageE, MessageF, MessageG, MessageH, MessageI, MessageJ, MessageK, MessageL>
[img]http://public.gamedev.net/public/style_emoticons/default/huh.gif[/img]

[/quote]

Yes, the memory is indeed something that bugs me... In the situation I used it, the types of all listeners were static, so I didn't have to use virtual functions there.
What exactly do you mean by MessageHandlers? Subclassing existing messages? (sry, I'm not very familiar with patterns since I'm still in school)

Share this post


Link to post
Share on other sites
Frank Taylor    191
My Engine revolves around its [url="http://www.gamedev.net/topic/606675-the-entire-3d-game-world-is-a-graphical-user-interface"]GUI[/url]. I'm closer to electing the [url="http://en.wikipedia.org/wiki/Publish/subscribe"]publish/subscribe[/url] model for message handling. The [i]Pubscriber [/i]is implemented as a specialize entity component (module).

[img]http://wiki.hpquest.com/images/6/66/Pubsub.png[/img]

1. Create a Channel.
2. Build Subscriber List on Channel; Channel Maintains list of of Subscribers.
3. Build Message.
4. Publisher Submits Message to Channel.
5. Channel Pushes Message to Subscriber's Message Queue.
6. Clear Channel.

Share this post


Link to post
Share on other sites
sjaakiejj    130
[quote name='T e c h l o r d' timestamp='1312986020' post='4847169']
I'm closer to electing the [url="http://en.wikipedia.org/wiki/Publish/subscribe"]publish/subscribe[/url] model for message handling.

[img]http://wiki.hpquest.com/images/6/66/Pubsub.png[/img]

1. Create a Channel.
2. Build Subscriber List on Channel; Channel Maintains list of of Subscribers.
3. Build Message.
4. Publisher Submits Message to Channel.
5. Channel Pushes Message to Subscriber's Message Queue.
6. Clear Channel.
[/quote]

I quite like this approach as well, though I'm slightly unclear about Channels. From what I gather you can make a comparison to a forum by saying:

Publishers are posters (Me in this topic)
Messages are topics
Channels are the different sections on the forum (Game Programming, Graphics Programming, etc.)

A subscriber then simply looks at the sections he's interested in, and selects the messages that he can use. If that analogy is about right, then I don't believe this approach should be very hard to implement. You could have a templated channel that accepts messages of a certain type that is specified in the template. Publisher submits them to the channel, the Channel forwards them to the subscribers.

Share this post


Link to post
Share on other sites
speciesUnknown    527
[quote name='GorbGorb' timestamp='1312985766' post='4847164']
[quote name='speciesUnknown' timestamp='1312983628' post='4847151']
Your example pretty much demonstrates exactly what I would do. Template based message filtering is one of the things I tried but again, for [b]simplicity[/b] i recommend the message ID + casting method. The amount of boilerplate required to handle messages in a simple scenario seems to be increased, but as your number of message types increases, your template based version will use huge amounts of memory and be considerably complicated. I'm not sure how you would implement decorators, relays, or other types of message handlers with the templated version either - would it require you to do something like,
MessageHandler<MessageA, MessageB, MessageC, MessageD, MessageE, MessageF, MessageG, MessageH, MessageI, MessageJ, MessageK, MessageL>
[img]http://public.gamedev.net/public/style_emoticons/default/huh.gif[/img]

[/quote]

Yes, the memory is indeed something that bugs me... In the situation I used it, the types of all listeners were static, so I didn't have to use virtual functions there.
What exactly do you mean by MessageHandlers? Subclassing existing messages? (sry, I'm not very familiar with patterns since I'm still in school)
[/quote]

Don't worry about design patterns. By message handlers, I was talking about the objects which receive messages and do things with them. There are a variety of different things which might be done with a message.

One handler may directly act when it receives a message - for example, your main game class may exit the game when it gets a certain message.

Another type of handler you might want to use is a filter, which only passes certain message types through.

A third type of handler you might have is a relay - a message handler which, when it receives messages, passes that message to a list of other handlers. For example, if you have an AI controller which has a list of AI creatures which are hunting the player. When one of these AI creatures sees the player, it needs to inform the others where it saw them, so it might send a message to the relay which then sends it to all the appropriate AI creatures in that area.

You might want to put a filter on top of your relay, to cut out messages which are never going to be handled by the AI creatures, thereby cutting out some of the overhead of handling messages which are irrelevant.

A flexible message system enables you to implement all these different types of message handling routines. What is important is not how cool or sophisticated the low level message handing mechanism is, but rather, what high level functionality you need, especially filters and relays.

I got frustrated with template based message relays because I found that I spent a silly amount of time debugging them. C++ does not have an efficient RTTI mechanism, or any kind of dynamic dispatch, so I've found that manual dynamic dispatch and manual type testing is the simplest solution.

Share this post


Link to post
Share on other sites
sjaakiejj    130
If I were to go with the approach that speciesUnknown mentioned, I'd prefer using a more type-safe pointer than a void. I looked at which functionality Boost might offer, and I came across boost::any and boost::variant, either of which seem to do the things I want them to. Are there any particular disadvantages over using them instead of void*?

Share this post


Link to post
Share on other sites
Frank Taylor    191
[quote name='sjaakiejj' timestamp='1312986657' post='4847172']
I quite like this approach as well, though I'm slightly unclear about Channels. From what I gather you can make a comparison to a forum by saying:

Publishers are posters (Me in this topic)
Messages are topics
Channels are the different sections on the forum (Game Programming, Graphics Programming, etc.)

A subscriber then simply looks at the sections he's interested in, and selects the messages that he can use. If that analogy is about right, then I don't believe this approach should be very hard to implement. You could have a templated channel that accepts messages of a certain type that is specified in the template. Publisher submits them to the channel, the Channel forwards them to the subscribers.
[/quote]
sjaakiejj,

I edited the post to add some more info: My Engine revolves around its [url="http://www.gamedev.net/topic/606675-the-entire-3d-game-world-is-a-graphical-user-interface/page__view__findpost__p__4837713__fromsearch__1"]GUI[/url]. The Messaging System called [i]Pubscriber [/i]is implemented as a specialized [url="http://wiki.hpquest.com/images/6/6d/Components.png"]entity component[/url] (module). Based on the wiki [url="http://en.wikipedia.org/wiki/Publish/subscribe#Message_filtering"]publish/subscribe[/url] article, the model provides a mechanism for selecting messages for reception and processing called [b][i]message filtering[/i][/b]. There are two common forms of filtering: topic-based and content-based:

[list][*][b]topic-based[/b] system, messages are published to "topics" or named logical channels. Subscribers in a topic-based system will receive all messages published to the topics to which they subscribe, and all subscribers to a topic will receive the same messages. The publisher is responsible for defining the classes of messages to which subscribers can subscribe.[*][b]content-based[/b] system, messages are only delivered to a subscriber if the attributes or content of those messages match constraints defined by the subscriber. The subscriber is responsible for classifying the messages.[*][b]topic-content hybrid[/b] system; publishers post messages to a topic while subscribers register content-based subscriptions to one or more topics.[/list]
I'm pursuing topic-content hybrid filtering. Entities are components-based and the Messaging System (Pubscriber) Components {Publishers, Subscribers, Channels} are automatically added to an entity upon creation to handle inter-communications between other Components.

Pubscriber components are inspired by the [url="http://en.wikipedia.org/wiki/Event-driven_architecture#Event_flow_layers"]event-driven architecture event-flow layers[/url]. The Publisher (Generator), Channel, Subscriber (Processor). The Subscriber component maintains a message queue and processes the messages.

Share this post


Link to post
Share on other sites
GorbGorb    112
[quote name='speciesUnknown' timestamp='1312986901' post='4847174']
Don't worry about design patterns. By message handlers, I was talking about the objects which receive messages and do things with them. There are a variety of different things which might be done with a message.

One handler may directly act when it receives a message - for example, your main game class may exit the game when it gets a certain message.

Another type of handler you might want to use is a filter, which only passes certain message types through.

A third type of handler you might have is a relay - a message handler which, when it receives messages, passes that message to a list of other handlers. For example, if you have an AI controller which has a list of AI creatures which are hunting the player. When one of these AI creatures sees the player, it needs to inform the others where it saw them, so it might send a message to the relay which then sends it to all the appropriate AI creatures in that area.

You might want to put a filter on top of your relay, to cut out messages which are never going to be handled by the AI creatures, thereby cutting out some of the overhead of handling messages which are irrelevant.

A flexible message system enables you to implement all these different types of message handling routines. What is important is not how cool or sophisticated the low level message handing mechanism is, but rather, what high level functionality you need, especially filters and relays.

I got frustrated with template based message relays because I found that I spent a silly amount of time debugging them. C++ does not have an efficient RTTI mechanism, or any kind of dynamic dispatch, so I've found that manual dynamic dispatch and manual type testing is the simplest solution.
[/quote]

What do you think about this? Have you faced problems where this is not flexible enough?
(I've not yet implemented it, I first want to know what you think about it)

[code]
struct MA{};
struct MB{};
struct MC{};

typedef Set< MA , MB , MC > Messages;

int main()
{
ListenerA first_listener;
ListenerB second_listener;
Relay< Messages > relay;
relay.addListener( first_listener );
relay.addListener( second_listener );
//all message sent to relay are forwarded to first_listener and second_listener
Filter< Messages , MessageB , MessageC > filter( relay ); //sends all messages except MessageB and MessageC a listener ( here: relay )
filter.listen( MessageA() ); //first_listener and second_listener receive MessageA
filter.listen( MessageB() ); //message is filtered away
};
[/code]

I really like the idea of filters and relays... how do you use them?

Share this post


Link to post
Share on other sites
speciesUnknown    527
[quote name='GorbGorb' timestamp='1313011287' post='4847332']
...

I really like the idea of filters and relays... how do you use them?
[/quote]

Basically, anything which can handle messages must implement an interface (or in c++, an abstract base class) IMessageHandler. Here is some psudocode:


[code]
// lets say this is your enum of message types. There are many other data types you can use, but enums are the simplest. You could also use some form of type ID, or use multiple enum types to categorise your messages - this example uses only 1.

enum MessageType
{
GameExit,
AIPlayerSeen,
AIPlayerKilledNPC,
AIPlayerDied
}

// this is the interface:
interface IMessageHandler
{
public:
void handleMessage(MessageType type, void * data) = 0;
}

// this is one possible message filter, to filter out only AI messages. This could also be done via a range comparison.
class AIMessageFilter : public IMessageHandler
{
protected:
IMessageHandler * output;

public:

AIMessageFilter(IMessageHandler * output)
: output(output) { }

void handleMessage(MessageType type, void * data)
{
switch(type)
{
case (AIPlayerSeen):
case (AIPlayerKilledNPC):
case (AIPlayerDied):
this->output->handleMessage(type, data);
break;
default:
//
break;
}
}
}

[/code]


edit: ok, this isnt actually psudocode, but ill pretend its psudocode, as it probably won;t compile xD

Share this post


Link to post
Share on other sites
speciesUnknown    527
[quote name='GorbGorb' timestamp='1313011287' post='4847332']


...

I really like the idea of filters and relays... how do you use them?
[/quote]

Basically, anything which can handle messages must implement an interface (or in c++, an abstract base class) IMessageHandler. Here is some psudocode:


[code]
// lets say this is your enum of message types. There are many other data types you can use, but enums are the simplest. You could also use some form of type ID, or use multiple enum types to categorise your messages - this example uses only 1.

enum MessageType
{
GameExit,
AIPlayerSeen,
AIPlayerKilledNPC,
AIPlayerDied
}

// this is the interface:
interface IMessageHandler
{
public:
void handleMessage(MessageType type, void * data) = 0;
}

// this is one possible message filter, to filter out only AI messages. This could also be done via a range comparison.
class AIMessageFilter : public IMessageHandler
{
protected:
IMessageHandler * output;

public:

AIMessageFilter(IMessageHandler * output)
: output(output) { }

void handleMessage(MessageType type, void * data)
{
switch(type)
{
case (AIPlayerSeen):
case (AIPlayerKilledNPC):
case (AIPlayerDied):
this->output->handleMessage(type, data);
break;
default:
//
break;
}
}
}

[/code]

Share this post


Link to post
Share on other sites
speciesUnknown    527
Here is how you would use a relay. Its simple really. It contains a mechanism to ensure a handler is not put in there twice. I'll leave the internals to the OP to implement.

[code]

class MessageRelay : IMessageHandler
{
protected:
deque<IMessageHandler *> handlers;
public:
void addHandler(IMessageHandler * h)
{
// check the handler isnt already there, return if it is
// put handler in the first empty slot
// if there is no empty slot, put it at the end
}

void removeHandler(IMessageHandler * h)
{
// iterate handlers, null out h when it is found
}

void handleMessage(MessageType type, void * data)
{
// iterate all handlers, call handleMessage on each one
}

}

[/code]


Here is how you would attach a filter to a message handler. This resembles the decorator pattern.

[code]

MessageRelay ai_relay;

AIMessageFilter ai_filter(&ai_relay);

AICreature creature1;
AICreature creature2;
AICreature creature3;

ai_filter.addHandler(&creature1);
ai_filter.addHandler(&creature2);
ai_filter.addHandler(&creature3);
[/code]

now, when you pass a message to the filter, only correct messages to to the relay, cutting down on the number of pointless calls to AICreature.handleMessage.

In this example, i'm declaring the objects by value and using pointers when things reference each other, but in real life I would be using shared_ptr and weak_ptr throughout the whole system.

You might also do something like adding the AI filter to a "main" relay, perhaps a relay owned by your world class. or, the AI relay and filter might be owned by a class which controls your AI system.

Share this post


Link to post
Share on other sites
_the_phantom_    11250
[quote name='speciesUnknown' timestamp='1313018189' post='4847385']
Basically, anything which can handle messages must implement an interface (or in c++, an abstract base class) IMessageHandler. Here is some psudocode:
[/quote]

I'm not convinced this is a good idea; you imply a relationship among classes where one doesn't really exist.

A better solution would be a simple delegate function using whatever language construct supports it. In C# this would be a delgate, in C++ a boost::function or std::function would do the job. This makes the dispatch more flexible (you can now route to free functions as well as class functions) and allows you to chain context data into the call back via boost::bind or std::bind.

Once you know those functions the solution is no more complex than the inheritance solution, maybe even less so as it could require less boiler plate code.

The signature need be more more complex than [b]typedef std::function<void (payloadType*)> CallBackFunction [/b]for the 'basic' function.

[source]

void freeFunc(payloadType* msg)
{
// some code
}

enum MessageType
{
GameExit,
AIPlayerSeen,
AIPlayerKilledNPC,
AIPlayerDied
}

class Foo
{
void bar(payloadType* msg);
void other(payloadType* msg);

void RegisterCallBack(MessageSystem &msgSystem)
{
msgSystem->Register(freeFunc, GameExit);
msgSystem->Register(std::bind(Foo::bar, this, _1), AIPlayerSeen);
msgSystem->Register(std::bind(Foo::bar, this, _1), AIPlayerKilledNPC);
}
}

[/source]

Calling would then be a matter of;

[source]
class MessageSystem
{
typedef std::function<void (payloadType*)> MessageType
typedef std::list<MessageType> MessageList;
typedef std::map<MessageType, MessageList> MessageMap
MessageList messageMap;
void Dispatch(MessageType msgId, payloadType *msg)
{
MessageMap Itr = messageMap.find(msgId);
if(Itr != messageMap.end() && !Itr->empty())
{
std::for_each(Itr->begin(), Itr->end(), [&msg](MessageType &func) { func(msg); });
}
}
}
[/source]

If you don't want to use C++0x then you'd have to use one of the boost::bind or another method to call it directly from that loop, or indeed just do a standard for-loop over the collection.

Share this post


Link to post
Share on other sites
speciesUnknown    527
[quote name='phantom' timestamp='1313020661' post='4847405']
[quote name='speciesUnknown' timestamp='1313018189' post='4847385']
Basically, anything which can handle messages must implement an interface (or in c++, an abstract base class) IMessageHandler. Here is some psudocode:
[/quote]

I'm not convinced this is a good idea; you imply a relationship among classes where one doesn't really exist.

[/quote]

what do you mean? Why do I need a relationship between classes?

[quote]

A better solution would be a simple delegate function using whatever language construct supports it. In C# this would be a delgate, in C++ a boost::function or std::function would do the job. This makes the dispatch more flexible (you can now route to free functions as well as class functions) and allows you to chain context data into the call back via boost::bind or std::bind.

Once you know those functions the solution is no more complex than the inheritance solution, maybe even less so as it could require less boiler plate code.

The signature need be more more complex than [b]typedef std::function<void (payloadType*)> CallBackFunction [/b]for the 'basic' function.

[source]

void freeFunc(payloadType* msg)
{
// some code
}

enum MessageType
{
GameExit,
AIPlayerSeen,
AIPlayerKilledNPC,
AIPlayerDied
}

class Foo
{
void bar(payloadType* msg);
void other(payloadType* msg);

void RegisterCallBack(MessageSystem &msgSystem)
{
msgSystem->Register(freeFunc, GameExit);
msgSystem->Register(std::bind(Foo::bar, this, _1), AIPlayerSeen);
msgSystem->Register(std::bind(Foo::bar, this, _1), AIPlayerKilledNPC);
}
}

[/source]

Calling would then be a matter of;

[source]
class MessageSystem
{
typedef std::function<void (payloadType*)> MessageType
typedef std::list<MessageType> MessageList;
typedef std::map<MessageType, MessageList> MessageMap
MessageList messageMap;
void Dispatch(MessageType msgId, payloadType *msg)
{
MessageMap Itr = messageMap.find(msgId);
if(Itr != messageMap.end() && !Itr->empty())
{
std::for_each(Itr->begin(), Itr->end(), [&msg](MessageType &func) { func(msg); });
}
}
}
[/source]

If you don't want to use C++0x then you'd have to use one of the boost::bind or another method to call it directly from that loop, or indeed just do a standard for-loop over the collection.


[/quote]

You've created the overhead of a map lookup, and added the overhead of boost.function AND boost.bind. And you haven't addressed the main issue - you still need a static list of message types. Not to mention the additional dependency on boost, which the OP will now have to build and install, plus, you've now thrown away the ability to write filters and relays. You now also have to make your final consumers of the messages bind each and every message to a message handler, creating a two way dependency when previously there was only one.

Also, remember that boost::bind will keep a shared_ptr alive forever. Your system needs to include a method of detaching message handlers from the message dispatcher. All this, to escape the discomfort of using a reinterpret_cast?

As for the use of std::foreach, why do this? perhaps it saves you from typing a few lines of code. Here is a few lines which are VERY important to understand:

Typing is not the bottleneck of programming.
Typing is not the bottleneck of programming.
Typing is not the bottleneck of programming.
[edit]
As a final note, your message dispatcher basically has the same interface as the IMessageHandler interface - so the two systems are probably compatible anyway. If in one part of the system needs per-member-function messages, one could implement a delegate based system.

Share this post


Link to post
Share on other sites
_the_phantom_    11250
[quote name='speciesUnknown' timestamp='1313021389' post='4847414']
[quote name='phantom' timestamp='1313020661' post='4847405']
I'm not convinced this is a good idea; you imply a relationship among classes where one doesn't really exist.
[/quote]

what do you mean? Why do I need a relationship between classes?
[/quote]

As soon as you use inheritance you create a relationship between classes; I don't know why you need one but your code creates one. Now anything which uses that interface can replace any other object which uses that interface; IS-A relationship, OOP 101.

[quote]
You've created the overhead of a map lookup,
[/quote]

Firstly this is trival overhead; a lookup is going to be swamped by the cost of any code run to handle the message anyway.
Secondly, this isn't a hard and fast requirement; you could just as well have used a vector or deque and done a direct lookup based on ID if you know your message range is contiguous.
Finally; go ahead and show me a lower overhead way of dispatching a single message type to intrested parties. Currently none of your examples deal with this in a better manner (your filter code above only deals with a single message in and out to a single listener) or at all.

[quote]
and added the overhead of boost.function AND boost.bind.
[/quote]

No, I used std::function and std::bind, which are going to be in the finialised C++11 spec and are usable now from VS2010 and GCC.
There are no boost dependences in this code, however you could do the same without those standard library headers but it would take a bit more boilerplate code or using a 'fast delegate' class (See google for examples, plenty exist).

[quote]
And you haven't addressed the main issue - you still need a static list of message types.
[/quote]

My code was based on your code above which used an enum; there is no requirement for a static list of message types.
Message IDs can be generated anyway you want and the 'payload' type is nothing more than place holder code; this could be a struct or a void pointer type. I didn't specify and I hoped those reading would understand intent, I guess I should be clearer in future.

[quote]
you've now thrown away the ability to write filters and relays. You now also have to make your final consumers of the messages bind each and every message to a message handler, creating a two way dependency when previously there was only one.
[/quote]

No, filters and relays are still do able.
That said the former isn't really required; the map lookup acts as a filter so unless you have registered intrest you won't get the message anyway which does the same job as a filter but without the requirement to perform an extra virtual function call to get from the message handler to the filter class and then onto the next object in the chain.

Now that I think about it the latter isn't either; again if you register intrest you will get the message which does the same job as a Relay class.

Filters and relays could still be implimented by having multiple message System instances and chaining that way and when someone registers with a sub-message system it automatically registers interest with the parent system for that message so that message can be past down the chain.

[Message system]-->[sub message system]-->[sub message system]-->[N x intrested parties]

This is no different to your system in that you still have to chain, but to get the same flexibility you require an extra link in the chain to broadcast out before filtering;

[Message system]-->[relay]-->[N x filter]-->[relay]-->[N x intrested parties]

In your case when a message goes in it has to be relayed out to N filters before it can be discarded with no intrest; so thats 1+N function calls already; plus your filter is hard coded at each level (which introduces a static ID type which you appear to have complained about in my example...) as per the code you have presented. Any dynamic filtering is going to require a registery.. much like a map..
With my system you perform a map lookup at the top level and discover no interested parties and bail right away.

If one entity is intrested then your system has 1 call to get to the relay, 2N calls to get to filters and past them, then then a further N calls to any intrested parties; 1+2N+M in total, all virtual, to at least 2 different class types, maybe more, which will do bad things to your cache.
In my system we perform a map lookup, make 1 function call, map lookup, 1 function call.

The same pattern continues, but in general you pay a worst case for everything where as the 'worst case' for my system is a complete broadcast down all paths for all entities.
(The submessage system also opens up the posibility for 'local' message groups and messages to enter the message system lower down and be broadcast back up the chain and then back out again, however that would require a little more code than already shown).

Yes, the final consumers have to bind any message they are intrested in, however this is on the whole quite minor and could also be handled with a simple loop; either over a range of IDs and routing to one handler or a range of ID to function pointers. Either way it is minor overhead given the flexibility and lower overhead on the critical routing path.

I'm not sure where you get this 'two way dependancy' from; yes you have to register yourself but beyond the interface there is no dependancy and certainly no more so than your system; in fact there is less because the coupling is completely loose and there are no relationships implied between any classes registering themselves as listeners, unlike when you use inheritance which automatically creates an IS-A relationship.

[quote]
Also, remember that boost::bind will keep a shared_ptr alive forever. Your system needs to include a method of detaching message handlers from the message dispatcher. All this, to escape the discomfort of using a reinterpret_cast?
[/quote]

There has been no mention of shared_ptr, but if you want to keep banging on about that I'll address it.
If your throw a shared_ptr into a bind then yes, of course it will keep it alive while the result of the bind is alive; if you didn't expect this then you clearly need to step back and read the docs again because this is intended and not a problem. The problem is throwing around shared_ptr everywhere and expecting it to work as some kind of primative GC system or something. Give that you class should be registering and unregistering itself as required if the callback requires a pointer to the class then a normal 'this' pointer would be just fine; once you go out of scope the callback won't be valid so if it gets called then you have an error.

In short; non-issue, use shared_ptr properly and you won't hit a problem.

And yes, of course you need to detach things; if you are basis this 'complaint' on the code I provided.. well I wrote that in about 2mins off the top of my head and it wasn't intended to be fully functional just demonstrate the problem.

Finally; what reinterpret_cast? I've not seen one nor have I complained about one... Feel free to explain this futher.

[quote]
As for the use of std::foreach, why do this? perhaps it saves you from typing a few lines of code. Here is a few lines which are VERY important to understand:

Typing is not the bottleneck of programming.
Typing is not the bottleneck of programming.
Typing is not the bottleneck of programming.
[/quote]

std::foreach is called C++ programming.
It has nothing todo with 'saving a few lines of code' and has todo with using the standard library; only in the C++ world is the usage of standard library functions considered a 'problem' which is crazy.
(Also, fun fact, most people write for-loops using containers wrong so they probably should be using std::foreach)

Also, feel free not to try and be condescending; I've got more experiance programming than you and frankly I'm better at it.
Want to be a dick with your 'typing is not the bottleneck of programming' line? Fine but try and find someone who can't take your arguments apart with hardly a thought.

[quote]
As a final note, your message dispatcher basically has the same interface as the IMessageHandler interface - so the two systems are probably compatible anyway. If in one part of the system needs per-member-function messages, one could implement a delegate based system.
[/quote]

Same interface.. just better, lower overhead for broadcasting, more flexible, does things you have to invent classes for.. if anything the solution would go the other way; If you don't want to bind to seperate function then bind one function to multiple intrested IDs and then switch in that funcion.
This would of course require that you drag the message ID along with the message, however that could be done a few ways.

[source]
std::foreach(startID, endID, [&messageSystem](unsigned int val) { messageSystem->Register(std::bind(Foo::bar, this, _1), val)});

void Foo::bar(unsigned int val, payloadType* data)
{
switch(val)
{
case 0: // blah
case 1: // blah
// etc
}
}
// or
struct payloadType
{
unsigned int val;
void * data
}

void Foo::bar( payloadType* data)
{
switch(data->val)
{
case 0: // blah
case 1: // blah
// etc
}
}
[/source]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this