0

I have a system, that receives messages (data chunks with a type) from somewhere (in this case network). Those are stored in a queue once received. Then these messages should get dispatched to handler functions and this got to happen fast (lots of messages)

Currently the system is designed in a way, that each Message type is an own class and overwrites a virtual function run(Handler&) which basicly calls the correct method in the handler. E.g.:

class PingMessage: public Message{
    ... // Some member variables
    void run(Handler& handler){
      handler.handlePing(*this);
    }
}

class Handler{
  void handlePing(const PingMessage& msg){...}
}

In this design, the Queue deletes the message, after it got dispatched. The problem is: Some handler functions need to store the message to execute them at a later time. Copying the message is not only a waste of memory and time (it gets deleted right after dispatch) but also not possible sometimes (complex deserialized data structures) So best would be to pass the ownership over to the handler.

I have the feeling, there is a design pattern or best practice for this. But I can't find it.

What I could imaging is calling a generic handler function "handleMessage(Type, Message*)" that switches on the type and does a dispatch with the Message static_cast'ed to the right type. Then it is clear by the convention of passing a pointer, that the handler is responsible for deleting the message. Maybe even use a base class, that does the switch and implements all handler functions empty. If a handler functions returns true, the handleMessage function deletes the Message, otherwise it assumes, the callee stored it somewhere. But I'm not sure if this is the right approach or if it incurs to much overhead. There seems to be to much room for errors.

Especially as I would have to do 2 checks for the Message Type: One for choosing the correct class to deserialize and one for calling the correct function.

Note: No C++11 available.

Sidenote: There is also something else to it: Most handlers just handle the message. So creating it on the heap with new and freeing it right after that is propably quite slow (mostly very small messages with just a couple of bytes) Using a handler, that deserializes the messages into stack based objects would be better, but then I'd have to copy them again which I can't. So shall I pass the raw message to the specific handler function and let them do deserialization as they wish? That means lots of duplicate code for different handlers... What do to here???

Flamefire
  • 5,313
  • 3
  • 35
  • 70
  • possible duplicate of [How to "return an object" in C++?](http://stackoverflow.com/questions/3350385/how-to-return-an-object-in-c) – Chemistpp Aug 01 '15 at 22:35
  • I've always gone with 'always give the handler ownership along with the message'. You always know where you stand then. If, at some later time, you decide to, say, forward the message on to some logger, you only have to think about ownership where you are and not worry about some upstream handler deleting it. – Martin James Aug 01 '15 at 22:57
  • @Chemistpp: How is this about returning an object? This is only a small part of the problem.
    So pass pointers to the handler function and delete it there? And got any hints on the sidenote?
    – Flamefire Aug 01 '15 at 23:05
  • Seems like the guy is also talking about memory management and ownership of returning a new instance of an object. Should he pass an object (maybe a msg) by reference or should he create the object and pass back the address and allow the caller manage the memory. Seems like the heart of this question, maybe I missed it. Also, the answer specifically discusses if copying is really all that ineffecient. Just how I read it. – Chemistpp Aug 02 '15 at 00:01

1 Answers1

1

Even though you indicate that you do not have C++11, it does not take a lot of code to implement your own C++03-compatible equivalent of std::shared_ptr. If your application is not multi-threaded, you won't even need to worry about updating the object's reference count in a thread-safe manner.

I don't use Boost, so I can't say authoritatively, but it's possible that Boost might already have a C++03-compatible implementation of std::shared_ptr that you can use.

Most modern implementations of memory allocators are actually quite efficient, and instantiating a new message object on the heap isn't as big deal as you think.

So, your overall approach is:

  1. You receive the message, and instantiate the appropriate subclass of Message on the heap.

  2. The run() method should also receive a reference-counted handle to the message itself, which it passes to the handler.

  3. If the handler does not need to save a copy of the message, it does nothing, and it will be destroyed soon thereafter, otherwise it grabs the reference handle, and stashes it away, someplace.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Ok. Is there a reference counted implementation that is instrusive? Otherwise, where does the counter live? Allocating it on the heap (4 Byte) would almost certainly be slow... Could you also comment on the dispatching method? So bascily virtual method in message class vs. enum switch vs. Enum switch the gets the raw message and creates the appropriate message. – Flamefire Aug 02 '15 at 10:07
  • Allocating it on the heap would indeed be slow. Incidentally this is what std::shared_ptr does, and what boost's precursor to std::shared_ptr does, which I always thought was the wrong design. I believe that the right approach to a reference-counted object implementation is to store it in a superclass, that reference-counted objects derive from, like what Java does. As far as dispatching, virtual method dispatching always seemed cleaner to me, even though a switch-based approach would probably be slightly faster. I always favor clean, maintainable code, to saving a few nanoseconds. – Sam Varshavchik Aug 02 '15 at 11:05
  • Boost does have an `intrusive_ptr` which is the perfect fit for this. So a virtual dispatching coupled with a ref-counted smart ptr which is implemented via intrusive_ptr gives the best maintainability and reasonable speed (ref-counter is stored in the class itself) – Flamefire Sep 30 '15 at 09:39