I find myself constantly running into a situation where I have a set of messages that I need to send over a TCP/IP connection. I have never found a good solution for the design of the message class. I would like to have a message base class where all messages derive from it. Since each message will have different fields, this would allow me to access the fields through member variables or methods. Something like...
class message_base
{
public:
message_base();
virtual ~message_base();
unsigned int type;
};
class message_control : public message_base
{
public:
message_control();
virtual ~message_control();
unsigned int action;
};
This way I can create a message_control and access the action member for assigning to and reading from. I can also pass the messages around without writing too much code.
The problem arises when I need to send the messages. If I override the operator<< and operator>> then I can send the messages over one variable at a time. The problem with that solution is that with so many calls to send data, the context switches will slam the processor. Also, the streaming operator ends up the the socket class and not in the message class where I would prefer it lived.
socket& socket::operator<<(message_control& message)
{
sock << type;
sock << action;
}
If I pack the data in a buffer, I get away from C++ and more into the realm of C and find myself making generous use of pointers and the like. And, modifying the code is difficult and error prone. And, the streaming operator is still in the socket class and not the message class.
socket& socket::operator<<(message_control& message)
{
byte* buffer = new byte[sizeof(message.type) + sizeof(message.action)];
memcpy(buffer, message.type, sizeof(message.type));
memcpy(buffer + sizeof(message.type), message.action, sizeof(message.action));
sock.send(buffer);
}
My last attempt used an intermediate class to handle packing and unpacking the members in a buffer. The messages could implement operator<< and operator>> to the buffer class and then the buffer class is sent to the socket. This works but doesn't feel right.
class socket
{
public:
socket();
~socket();
socket& operator<<(buffer& buff);
};
class buffer
{
public:
buffer() {m_buffer = new byte[initial_size];}
~buffer() {delete [] m_buffer;}
buffer& operator<<(unsigned int value);
private:
byte* m_buffer;
};
void message_control::serialize(buffer& buff)
{
buff << type;
buff << action;
}
I can't help but feel there is an elegant solution to this problem. I can't find any design patterns that match what I am trying to accomplish. Has anyone experienced this problem and come up with a going design that doesn't make you feel like you would be better off with good old pointers and an array of bytes?
Update
I failed to mention in my original post that I am most often dealing with very well define wire protocols. That is why I typically need to roll my own solution and can't use any of the wonderful toolkits available for messaging over a network connection.