Jump to content
  • Advertisement
Sign in to follow this  
DaveMS

Message class

This topic is 3407 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, I'm currently using a messaging system, and i'm thinking of a way to extend the functionality of the messages which are passed around. At the moment, i'm just using a very simple struct for the message data. While this works ok, there are obvious problems. I can't store anything other than ints in the message. I can't store any more than 3 values. If I store less than 3 values, it seems like a waste:

struct Message
{
  Msg_Type msgType;   // enum - message type
  int senderID;
  int value1;
  int value2;
  int value3;

};

Obviously, I could add more variables to the message, and now I can add a string to the message, and/or a float etc. But this doesn't seem like a great way of doing it. I'm unlikely to need to use all those data types in one message, i'll likely only use one, setting the rest to 0 or NULL etc:

struct Message
{
  Msg_Type msgType;
  int senderID;
  int value1;
  int value2;
  int value3;
  std::string value4;
  float value5;
  // blah blah....
}

Here's an idea I haven't actually tested, but the basic idea is below. The idea is to compose the message of message components. This way I could add a StringMessageComponent to the Message, and an IntMessageComponent. Or 4 IntMessageComponents. Or so on... values could be retrieved by iterating through the msgComponents vector and calling the GetValue() method on each component. Things could get very messy though if the value returned by the message component isn't the data type the object reading the message was expecting:

class MessageComponent
{
public:
  virtual void* GetValue()=0;
};

class IntMessageComponent : public MessageComponent
{
private:
  int value;

public:
  void* GetValue(){return value;}
};

class StringMessageComponent : public MessageComponent
{
private:
  std::string value;

public:
  void* GetValue(){return value;}
}

//.... class FloatMessageComponent
//... class CharMessageComponent
//... etc...

class Message
{
  Msg_Type messageType;
  std::vector<MessageComponents*> msgComponents;

  AddIntMessageComponent(int value);
  AddStringMessageComponent(std::string value);
 // etc..

};


I guess what i'm really asking is (and I probably should have just asked the question rather than writing a wall of text): Can anyone think of a way to store a variable number of different data types? eg. 2 ints and 1 float or 1 string and 1 float.

Share this post


Link to post
Share on other sites
Advertisement
Your message component idea isn't bad, although every time you want to add a different type of component you'll have to create a new class and a new function in your Message class. This may not be a problem depending on your requirements, but it makes your message class hard to extend.

What I would do is use void pointers and have the calling code receiving the message processing code just cast back to the data type it plans on using. This may be a problem if the message is processed outside of the scope of the data passed in though. All my messaging systems process the message at the time it is sent.

Share this post


Link to post
Share on other sites
Surely just having it store a vector of whatever value types you'd need'd be a bit more generic? You could try passing the data as a string and re-interpreting as required?

Share this post


Link to post
Share on other sites
struct Message {
Msg_Type type;
void *data;
};


or, if there's fewer variations or they're all near the same size or you don't care
struct Message {
Msg_Type type;
union {
struct {
int t1_var;
} t1;
struct {
char t2_name[16];
int t2_var;
} t2;
};
};


or, use templates

Share this post


Link to post
Share on other sites
Thanks everyone.

I think i'm making it more complicated than it needs to be.

I think a vector of void pointers is all thats needed.


struct Message
{
Msg_Type type;
std::vector<void*> values;
};



Thanks for the help everyone.


Share this post


Link to post
Share on other sites
void* are almost never the right answer in C++. If you must, consider using something like boost::any which handles it behind the scenes for you.

The loss of type sanity with a void* is one thing, but often more critical an issue in these sorts of scenarios if the fact that you have a pointer, which means that you have lifetime concerns. You can't just create a message anywhere, you have to make sure the parameters to that message will outlive the message itself, which can become difficult, tedious, and unnecessarily coupling:

void F() {
int i = 10;
Message m;
m.values.push_back( /* ??? */ );

// ...
}

You can't just pass &i here to store i. You have to heap-allocate it via new to ensure that it will live long enough; this means then that something has to clean the message parameters up, typically the message processor, but then that means the processor has to know whether to delete or delete[] (or do nothing at all, since maybe the parameter was a pointer to a static or global with extended lifetime), et cetera.

A union is the more common technique used to avoid this problem, since that way you can store the appropriate type by value.

Share this post


Link to post
Share on other sites
I would recommend against void *pointers.

Simply have an enum to show the message type, then inheritence for the data.

enum MessageTypes {MT_CALLBACK};

class Message
{
public:
MessageTypes MessageType;
};
class MessageCallback : public
{
public:
boost::function<void()> Call;
};


//message handler
void Handler(Message *msg)
{
switch(msg->MessageType)
{
case MT_CALLBACK:
((MessageCallback*)msg)->Call();
break;
}
}




You could also use the constructors to set the MessageType to help avoid the issue of the actauly instance and the MessageType not matching.

Share this post


Link to post
Share on other sites
How and where do you plan to use this messaging system? Perhaps providing some use cases here can result in more specific suggestions.

Share this post


Link to post
Share on other sites
Quote:
Original post by Captain P
How and where do you plan to use this messaging system? Perhaps providing some use cases here can result in more specific suggestions.



The classes below are what currently make up my messaging system.

The messaging system is currently used for informing objects of game events... key pressed / key released / game start/end etc.

For example, lets say we've got a message which is sent out when a collision between 2 GameObjects occurs. The message type Msg_Collision is stuffed into a message, along with some data which the receiving objects may need.. maybe position of collision and the GameObjects involved. The message is dispatched through the MessageDispatcher class, and any objects subscribed to receive this message type receives a call to their ReceiveMessage() method (inherited from the abstract MessageHandler class), and the message is passed as a parameter.

Back to the collision example, there may be a number of objects subscribed to the Msg_Collision message -

The SoundBank class will receive the message and play a collision sound at the position the collision occured, or maybe depending on the type of GameObjects involved.

The ParticleSystem class will be subscribed in order to create a "spark" particle effect at the point of collision.

The GameObjects themselves will be subscribed to receive the collision message to adjust their position, reduce their health etc.. maybe one of the GameObjects, due to the collision, loses all it's health, and dies. A Msg_Die message is sent to the message dispatcher, with the GameObject ID and position, and there will be obviously classes which would be interested in this Die message.


Sorry for the huge wall of text there.

The short version of the above - The messaging system is used for pretty much everything.

The obvious advantage of this I think is that the CollisionDetection class doesn't need to know about the ParticleSystem class, SoundBank class etc..
It just throws out a message when a collision occurs with all the relevant data needed.


oh yeah... the classes below..

MessageDispatcher - stores Messagehandlers in a map, with the Msg_Type as key.
(actually, it stores MessageHandler pointers in a vector in a map, with the Msg_Type as the key.

MessageHandler is just an interface for any classes which want to receive messages. They need to implement the ReceiveMessage method, obviously.

The Message struct is how my messages currently look.


If anyone has actually gone to the trouble to read all this... thanks alot :)


#ifndef MESSAGEDISPATCHER_H
#define MESSAGEDISPATCHER_H

#include <map>
#include <vector>
#include "MessageHandler.h"
#undef SendMessage

class MessageDispatcher
{
private:

static MessageDispatcher* _this;
std::map<Msg_Type, std::vector<MessageHandler*>>* handlers;
MessageDispatcher();

public:

~MessageDispatcher();
static MessageDispatcher* getInstance();
void Subscribe(MessageHandler* hndr, Msg_Type msgType);
void UnSubscribe(MessageHandler* hndr, Msg_Type msgType);
void SendMessage(Message msg);

};
#endif




#ifndef MESSAGEHANDLER_H
#define MESSAGEHANDLER_H

#include "Message.h"

#undef SendMessage

class MessageHandler
{
public:
~MessageHandler(){}
virtual void RecieveMessage(Message msg)=0;
};
#endif





#ifndef MESSAGE_H
#define MESSAGE_H

#include "MessageType.h"
#include <vector>

struct Message
{
Msg_Type type;
int sender;
int value1;
int value2;
int value3;

};
#endif




#ifndef MESSAGETYPE_H
#define MESSAGETYPE_H

enum Msg_Type
{
Msg_GameStart,
Msg_NewVelocity,
Msg_Collision,
Msg_Quit,
Msg_KeyDown,
Msg_KeyUp,

};
#endif

Share this post


Link to post
Share on other sites
I wonder why everything needs to be stuffed into one generic message type. What do you gain by making this uniform?

The event passing mechanic itself is likely the same for all types, but that can be templatized: EventDispatcher<CollisionEvent>, EventDispatcher<InputEvent>, EventDispatcher<GameEvent>, and so on. You write the EventDispatcher<T> and EventListener<T> code once and it's reusable for all event types.

The messages themselves however contain different information and will be used for different purposes, so why stuff them all into a single, generic message type, only to decipher their meaning later on? Why not use C++'s type system to your advantage? Just write one class per message type.

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!