Base class MessageHandler has derived classes. They would like to pass messages to each other. Messages could be of different classes, but can be made to share a base class. How can each MessageHandler avoid downcasting a received message? Is it somehow possible to do something that has the effect of template-parametrizing the virtual receiveMessage function on MessageHandler?
Essentially, I’m trying to replace the following code with something that does not downcast, and is hopefully a compile-time thing:
// ...
virtual void MessageHandler::receiveMessage(Message &msg) = 0;
// ...
// to receive a message
void DerivedMessageHandler::receiveMessage(Message& msg)
{
switch (msg.MsgType()) // enum
{
case Message::MessageType::A:
MessageA& = dynamic_cast<MessageA&>(msg);
break;
case Message::MessageType::B:
MessageB& = dynamic_cast<MessageB&>(msg);
break;
default:
// don't process unknown messages
break;
}
}
// to send a message
list<MessageHandler> mhList;
// populate list
for (MessageHandler& mh : mhList)
{
mh.receiveMessage(msg);
}
I know I can’t do this, but something like
template <typename M>
void MessageHandler::receiveMessage(M& msg) {}
And have each DerivedMessageHandler specialize on M? What would be a design pattern that cleanly lets each handler work on their supported message objects?
This is pretty easy to do. There are generally two alternatives:
Boost.Variant
Instead of passing a derived class, simply enumerate the possible types that a message could be. These types need not be derived from one another. Wrap those types in a boost::variant:
Note that this means that the possible message data types must be enumerable. Boost.Variant’s visitation methods make it easy to work with objects of these types without knowing exactly which type it stores.
Boost.Any
Simply pass anything with a
boost::any:boost::anyis like a type-safevoid*. It remembers the exact type that was put into it, and any attempt to cast it to something other than what is stored in it will fail.boost::anycan store anything, hence the name.It also has value semantics, so it can be copied like its contents.