Designing an efficient and low resource messaging framework. Part 1

I am going to show you three ways in which a messaging framework might be implemented; the first is the ‘proper’ idiomatic C++ way; in the second and third parts, a more efficient blending of C & C++ styles (but may make C++ purists choke on their breakfast).

The specification

  1. There are a set of N messages that can be passed around the application.
  2. The messages are all derived from a base message class.
  3. Objects in the application must be able to receive and process messages.
  4. Objects receiving messages are derived from a base message processor class.
  5. Message processors receive messages as references to the base message class.
  6. They will not all processes the same number or combination of messages.

Let’s start off with the pure object orientated C++ version.

It is usually deemed a ‘code smell’ in C++ programming if code has to interrogate a base object to determine its concrete derived type. As messages are passed to message processors as references to the base message type, some way must be created to steer the message to the correct handler. One way of doing this is to use ‘double dispatch’ or the Visitor Pattern.

Is you are not familiar with this pattern, then hopefully this metaphor will give you the basic idea.

Imagine a salesman visits a company. He meets management, sales, marketing and engineering. He gives out business cards to each person he meets. There is a different telephone help line for each type of recipient, one for management, one for sales etc. Now, he could have a different card for each type of recipient, which would require him to know the job title of each person he meets. He would have to ask them what they did and choose the correct card for each one.

Alternatively, he could have one card with all of the telephone numbers on it and tell each recipient to “call the help line appropriate to your job title”.

This is effectively what the visitor pattern achieves. It says to the message “Here is an interface of overloaded functions that can accept all of the messages. Call the one that matches your concrete type”.

An interface is created that defines a virtual handler function for every message. The handlers in the interface may have default bodies, maybe logging an unhandled message. The concrete message processor inherits from this and implements each of the message handlers it is interested in. When a reference to a base message object is received it will pass itself to the message and the message will call the correct handler in the message processor.
Neat!

Let’s show a little bit of code as an example.

The code below defines a visitor interface that defines virtual handlers for four message types. The virtual handlers have default bodies that send control to an ‘Unhandled’ member (pure virtual). Two message processors are defined, one handling all of the messages, the other just Message1 and Message3.

class IMessageProcessor;

struct IMessage
{
  virtual void Accept(IMessageProcessor& processor) const = 0;
};

struct Message1 : public IMessage
{
  void Accept(IMessageProcessor& processor) const;
};

struct Message2 : public IMessage
{
  void Accept(IMessageProcessor& processor) const;
};

struct Message3 : public IMessage
{
  void Accept(IMessageProcessor& processor) const;
};

struct Message4 : public IMessage
{
  void Accept(IMessageProcessor& processor) const;
};
class IMessageProcessor
{
public:

  void Receive(const IMessage& msg)
  {
    msg.Accept(*this);
  }

  virtual void Handle(const Message1& msg) { Unhandled(msg); }
  virtual void Handle(const Message2& msg) { Unhandled(msg); }
  virtual void Handle(const Message3& msg) { Unhandled(msg); }
  virtual void Handle(const Message4& msg) { Unhandled(msg); }

  virtual void Unhandled(const IMessage& msg) = 0;
};
void Message1::Accept(IMessageProcessor& processor) const
{
  processor.Handle(*this);
}

void Message2::Accept(IMessageProcessor& processor) const
{
  processor.Handle(*this);
}

void Message3::Accept(IMessageProcessor& processor) const
{
  processor.Handle(*this);
}

void Message4::Accept(IMessageProcessor& processor) const
{
  processor.Handle(*this);
}
class Processor1 : public IMessageProcessor
{
public:

  void Handle(const Message1& msg)
  {
    std::cout << "Processor1 : Message1\n";
  }

  void Handle(const Message2& msg)
  {
    std::cout << "Processor1 : Message2\n";
  }

  void Handle(const Message3& msg)
  {
    std::cout << "Processor1 : Message3\n";
  }

  void Handle(const Message4& msg)
  {
    std::cout << "Processor1 : Message4\n";
  }

  void Unhandled(const IMessage& msg)
  {
    std::cout << "Processor1 : Unhandled IMessage\n";
  }
};
class Processor2 : public IMessageProcessor
{
public:

  void Handle(const Message1& msg)
  {
    std::cout << "Processor2 : Message1\n";
  }

  void Handle(const Message3& msg)
  {
    std::cout << "Processor2 : Message3\n";
  }

  void Unhandled(const IMessage& msg)
  {
    std::cout << "Processor2 : Unhandled IMessage\n";
  }
};

Lets exercise this code.

int main()
{
  Processor1 p1;
  Processor2 p2;

  Message1 m1;
  Message2 m2;
  Message3 m3;
  Message4 m4;

  p1.Receive(m1); // "Processor1 : Message1"
  p1.Receive(m2); // "Processor1 : Message2"
  p1.Receive(m3); // "Processor1 : Message3"
  p1.Receive(m4); // "Processor1 : Message4"

  p2.Receive(m1); // "Processor2 : Message1"
  p2.Receive(m2); // "Processor2 : Unhandled IMessage"
  p2.Receive(m3); // "Processor2 : Message3"
  p2.Receive(m4); // "Processor2 : Unhandled IMessage"

  return 0;
}

The output of this test will be…

Processor1 : Message1
Processor1 : Message2
Processor1 : Message3
Processor1 : Message4
Processor2 : Message1
Processor2 : Unhandled IMessage
Processor2 : Message3
Processor2 : Unhandled IMessage

This is a neat solution and does not require any message processor to interrogate the received message to determine its type.

There an issue with this scheme that may impact the implementation on a resource limited embedded platform. The visitor interface IMessageProcessor must contain a virtual handler function for every message that exists. The vtable for this may become prohibitively large when there are a substantial number of message types. Each derived message processor will have its own copy of the vtable. This will be true even if the derived message processor only handles one message! This may invoke an unacceptable resource overhead.

Also there is the coupling between the visitor interface and the message types. It would be nice if we could make things a little more decoupled.

In part 2 I will describe the first alternative that uses templates and template specialisation to address some of these issues.

Download the example code

John Wellbelove

John Wellbelove

Director of Aster Consulting Ltd
United Kingdom
I have been involved in technology and computer systems for all of my working life and have amassed considerable knowledge of designing and implementing systems that are both performant and correct. My role normally encompasses the entire project life-cycle, from specification to maintenance phase. Most systems I have worked on have required high speed and deterministic performance, often within a highly constrained platform. I am experienced in designing and adapting algorithms to solutions that are both space and time efficient, avoiding the normal overheads of standard solutions. Acting as a mentor for colleagues has often been a significant, though unofficial, part of my role.

I administer an open source project on Github.
See http://www.etlcpp.com/

Leave a Reply

Your email address will not be published. Required fields are marked *