Simple Messaging Service

Environment: VC4-6

Message service of Windows is very inconvenient for using as own service due to the followings:

– you can either make your own messages higher WM_USER, but this code is not portable, or dynamically allocate message identificators, but this give awful operators if in DefWindowProc;

– you need inherit all message clients from CCmdTarget, but it can be inconvenient or impossible;

– you need to make potetially dangerous type casting from LPARAM to the real message pointer.

Now I introduce simple messaging service without this drawbacks. In this technique, message handlers get only messages of types declared immediately in their arguments. However, message sender has no need to know the whole message set proceeded by the handler and, consequentally, can safely send to it any kind of message.

I guess this technique can be most effectively used in the next areas

– PnP in the embedded systems;

– dynamic link of software parts, class ierarchies, patterns etc.;

– interaction with controls and their inheritance;

– the interactive media in Doc/View or MVC architecture.

This code illustrates using of service:


// main.cpp
//--------

#include "msg.h"

// For non-MVC compilers:
//-----------------------
#ifndef TRACE
#define TRACE void
#endif

// messages A,B,C
//------------------
class A:public Msg
{
public:
 A() : a(10) {};
 int a;
};

class B:public Msg
{
public:
 B() : b(11) {};
 int b;
};

class C:public Msg
{
public:
 C() : c(12) {};
 int c;
};

// Object capable to receive messages A and B
//---------------------------------------------
class Object: public Handler<A>,public Handler<B>
{
public:
 void OnNotify (const A&);
 void OnNotify (const B&);

};
// functions to receive messages
void Object::OnNotify (const A& msg)
{
 TRACE ("Object::OnNotify (const A& msg)n");
};

void Object::OnNotify (const B& msg)
{
 TRACE ("Object::OnNotify (const B& msg)n");
};


void fun (Target* pTarget)
{
 A msga;
 B msgb;
 C msgc;
   // send messages A,B,C to Target
   // (now the real type of target is not known)
 pTarget->Notify (msga);
 pTarget->Notify (msgb);
 pTarget->Notify (msgc); // this message cannot be received
}


void main ()
{
 Object ob;
 fun (&ob);
}

In this example object of type Object has handlers for messages of types A and B and object will get messages A and B and don’t get message C, in spite of the real type of hanler is not known in fun().

Technique based on the combination of templates, multiple inheritance and double-dispatchering.
All messages must be inherited from base class Msg, which does nothing, but contains only virtual destructor.
The object capable to receive messages (message target) must be inherited from the set of template classes Handler. The argument of template classes Handler points to the message. And, finally, object must have a set of functions void OnNotify (const Message&) for all messages. This functions declared in the template as pure virtual and compiler will generate error if you forget anything.


// msg.h
//
//============================================

#ifndef MSG_H__
#define MSG_H__ 1


//#define STL_STYLE 1 /* uncomment this line for using STL containers */

#include <typeinfo.h>

#ifdef STL_STYLE
#include <map.h>
#include <bstring.h>
#else
#include <afx.h>
#include <afxtempl.h>
#endif


class Msg
{
public:
 Msg(){};
 virtual ~Msg() {};
};

class HandlerBase;

// the base class for message target
// contains virtual table for dispatchering
//============================================
class Target
{

public:
 void Notify (const Msg& msg);

protected:
 Target() {};
 virtual ~Target() {};
 // Vtable for double dispatchered calls
 //============================================
#ifdef STL_STYLE
 typedef less<string> compare;
 class Container : protected map<string,HandlerBase*,
 compare>
 {
 public:
  Container() {};
  HandlerBase* Find(const string& classname);
  void Insert (const string& classname, HandlerBase* handler);
  void Erase  (const string& classname);
 };
#else
 class Container : protected CTypedPtrMap<CMapStringToPtr,
 CString, HandlerBase*>
 {
 public:
  Container() {};
  HandlerBase* Find(LPCTSTR classname);
  void Insert (LPCTSTR classname, HandlerBase* handler);
  void Erase  (LPCTSTR classname);
  ~Container() { RemoveAll();};
 };
#endif

 Container Vtable;

};

// auxilary abstract class for double dispatchering
class HandlerBase
{
protected:
 friend class Target;
// friend class Target::Container;
 virtual void IndirectCall (const Msg& msg)=0;

};


// template class for message handlers
//
// all message handlers have one copy of base
// class Target and multiple copies of HandlerBase for each
// ========================================================
template <class Message> class Handler :
 virtual public Target, private HandlerBase
{
public:
 Handler();
 virtual ~Handler();

 virtual void OnNotify (const Message&)=0;

protected:
  // function for double dispatchering
 virtual void IndirectCall (const Msg& msg)
 {
  OnNotify ((const Message&)msg);
 };
private:
#ifdef STL_STYLE
 string mMessageClass;
#else
 CString mMessageClass;
#endif

};

template <class Message>
Handler<Message>::Handler()
{
  // here defines personal entry to Vtable accordingly to
  // message class name and own copy of HandlerBase pointer
 Vtable.Insert (mMessageClass=typeid(Message).raw_name(),this);
};

template <class Message>
Handler<Message>::~Handler()
{  // remove Vtable entry
 Vtable.Erase(mMessageClass);
};


#endif
// msg.cpp
//--------

#include 
#include "msg.h"


void Target::Notify (const Msg& msg)
{
 HandlerBase* pthis = Vtable.Find(typeid(msg).raw_name());
 // find out the appropriate handler for given message type
 if (pthis!=0)
 {
   // indirectly call OnNotify through virtual
   // base class member
  pthis->IndirectCall(msg);
 }
 else
 {
  // do something if target have no handler for
  // dispatched message
  //...
 }

}

//----------------------------------
// Container for fast handler access
//----------------------------------

#ifdef STL_STYLE
HandlerBase* Target::Container::Find(const string& classname)
{ iterator it=find (classname);
 return (it!=end()) ? (*it).second : 0;
}

void Target::Container::Insert (const string& classname,
HandlerBase* handler)
{
 operator[](classname)=handler;
}

void Target::Container::Erase  (const string& classname)
{ erase (classname);
}

#else
HandlerBase* Target::Container::Find(LPCTSTR classname)
{ HandlerBase* pHandler;
 return Lookup(classname, pHandler) ? pHandler : 0;
}

void Target::Container::Insert (LPCTSTR classname,
HandlerBase* handler)
{
 operator[](classname)=handler;
}

void Target::Container::Erase  (LPCTSTR classname)
{ RemoveKey (classname);
}
#endif

Also you can supply this example by static function NotifyAll and container for all pointers to Target and will give a possibility to send notifications to all current message targets.

Some tips

1) For correct work you need RTTI support by compiler and if you use Visual C++ you must turn on the RTTI option

2) This example includes container based on old Standart Template Library from MVC 4.0 as well as container based on MFC template container classes. To use STL simple uncomment line

#define STL_STYLE 1

in file msg.h. But users of Visual C++ 4.2-6.0 must change appropriate directives #include and additional include directories in the project.

Downloads

Download source – 4 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read