First of all, I come from a bit higher level languages that can handle runtime decisions
pretty well and are easy to work with, so I'm having a lot of troubles in C++. Something
that seemt easy at first, turned out to be either impossible, or requiring some black
magic to achieve. Consider a simple network message dispatcher that receives a packet,
reads its type and depending on it creates a wrapper that contains specific data - each
network message would have a bit different data (sorry for complete pseudocode but I
don't have anything written yet because I hit the wall early on design stage and its
pointless to code when I know it won't work as I supposed it would..)
enum {
MSG_LOGIN,
MSG_DISCONNECT,
...
} MessageType;
// Base class
class Message
{
MessageType msgType;
MessageType GetMessageType();
SetMessageType(MessageType type);
SendMessage(); // sends message over the network
}
// Derived message class
class LoginMessage: public Message
{
string login;
string pass;
LoginMessage(Bitstream data)
{
login = data.ReadString(20);
pass = data.ReadString(20);
}
}
while (incoming packets)
{
// This creates message wrapper class that reads data from packet and sends
// it to factory method to create a specific message (LoginMessage, ConnectMessage)
Message* msg = msgfactory.CreateMessage(packet.type, packet.data)
// Publish it to the dispatch system, for now we just need to know that message
// will be send to one or more handlers which registered to receive messages of that
// type
MsgQueue.Publish(packet.type, msg);
}
Publish sends that message to all registered handlers, for example if packet.type is
MSG_LOGIN it would be handled by LoginManager::HandleLoginRequest (it registers itself
as a listener for this particular message type, but that part of code is irrelevant).
Lets look at this message receiving method:
LoginManager::HandleLoginRequest(Message* msg) // this so we can receive Message* from
// factory, but in reality this method is
// only interested in LoginMessage*
{
// PROBLEM: msg is base class so it doesn't have any knowledge of derived class
// members that I want to use
if (msg.login && msg.password) { ... } // won't work
// handle the message logic
}
HandleLoginRequest method will only be called with LoginMessage instances, because it
registers to receive only them, but I don't know how to make it work with:
HandleLoginRequest(LoginMessage* msg)
when I receive Message* from factory.
I could use dynamic_cast<LoginMessage*> at beginning of hanlder function to downcast
received base class to its derived class, but I only heard bad things about it, and
it doesn't seem clean anyway to do it for every handler method that receives message,
even if I'm sure that this message will be a certain derived class.
I find it very hard to write things that are decided on runtime in C++ - there is
always some problem.
There will be many messages each having its own wrapping class so I don't really
want to use enormous switch statement that will be able to determine on compile time
things like what class needs to be created.
Same problem I've encountered when I wanted simple event system, with each event
having its EventArgs class that can be derived to contain more specific information
(for example for DamageEvent it will be amount of damage, attacker etc., for DieEvent
it could be killer). Then again, events are usually dispatched in a manner:
HandleEvent(Event& e, EventArgs& a)
which again destroys the idea because if I derive MyEventArgs from EventArgs and I use
it together with my event, when it arrives at handler it wont be MyEventArgs anymore,
I will loose that additional part of data I needed.
Is there some clever workaround or I just do things in a non-C++ way? I hope I described the problem well enough, if not let me know and I will try to write something more than pseudo-code.