I wanted to allow components of my program to communicate without the components knowing about each other, thus keeping coupling low. So I devised some template classes that implement the observer pattern and allow classes to send messages of any type, providing they inherit from a message interface base class, and a message listener base class, paramaterized by the same message type (please feel free to correct my terminology here!)
Please ignore references to the LOG
macro; it's just something I use to debug.
#ifndef MESSAGING_HPP
#define MESSAGING_HPP
#define LOG(...)
#include <vector>
namespace messaging{
template<typename T>
class IMessageListener;
template <typename T>
class IMessageContext {
friend class IMessageListener<T>;
template <typename U>
friend void send(IMessageContext<U>* ptrContext, const U& _msg);
template <typename U>
friend void attach(IMessageContext<U>* ptrContext, IMessageListener<U>* ptrListener);
template <typename U>
friend void detach(IMessageContext<U>* ptrContext, IMessageListener<U>* ptrListener);
std::vector<IMessageListener<T>* > listeners;
void attachListener( IMessageListener<T>* _listener);
void detachListener( IMessageListener<T>* _listener);
void dispatch(const T& _msg);
public:
~IMessageContext();
};
template <typename T>
void IMessageContext<T>::attachListener( IMessageListener<T>* _listener)
{
LOG(__PRETTY_FUNCTION__);
listeners.push_back(_listener);
}
template <typename T>
void IMessageContext<T>::detachListener( IMessageListener<T>* _listener)
{
LOG(__PRETTY_FUNCTION__);
listeners.erase(std::remove(listeners.begin(), listeners.end(), _listener), listeners.end());
}
template <typename T>
void IMessageContext<T>::dispatch(const T& _msg)
{
LOG(__PRETTY_FUNCTION__);
typename std::vector<IMessageListener<T>* >::iterator i;
for(i = listeners.begin(); i != listeners.end(); ++i){
(*i)->handleMessage(_msg);
}
}
template <typename T>
IMessageContext<T>::~IMessageContext()
{
//detach from all listeners so they dont try to detach from this context when they are destructed
LOG(__PRETTY_FUNCTION__);
typename std::vector<IMessageListener<T>* >::iterator i;
for(i = listeners.begin(); i != listeners.end(); ++i){
(*i)->messaging::template IMessageListener<T>::detachContext(this);
}
}
template<typename T>
class IMessageListener {
friend class IMessageContext<T>;
template <typename U>
friend void attach(IMessageContext<U>* ptrContext, IMessageListener<U>* ptrListener);
template <typename U>
friend void detach(IMessageContext<U>* ptrContext, IMessageListener<U>* ptrListener);
std::vector<IMessageContext<T>* > contexts;
void attachContext( IMessageContext<T>* _listener);
void detachContext( IMessageContext<T>* _listener);
virtual void handleMessage(const T&) = 0;
public:
virtual ~IMessageListener();
};
template <typename T>
void IMessageListener<T>::attachContext( IMessageContext<T>* _context)
{
LOG(__PRETTY_FUNCTION__);
contexts.push_back(_context);
}
template <typename T>
void IMessageListener<T>::detachContext( IMessageContext<T>* _context)
{
LOG(__PRETTY_FUNCTION__);
contexts.erase(std::remove(contexts.begin(), contexts.end(), _context), contexts.end());
}
template <typename T>
IMessageListener<T>::~IMessageListener()
{
//detach from all contexts so they dont try to message missing listener
LOG(__PRETTY_FUNCTION__);
typename std::vector<IMessageContext<T>* >::iterator i;
for(i = contexts.begin(); i != contexts.end(); ++i){
(*i)->messaging::template IMessageContext<T>::detachListener(this);
}
}
// The real API
template <typename T>
void send(IMessageContext<T>* ptrContext, const T& _msg)
{
LOG(__PRETTY_FUNCTION__);
ptrContext->template IMessageContext<T>::dispatch(_msg);
}
template <typename T>
void attach(IMessageContext<T>* ptrContext, IMessageListener<T>* ptrListener)
{
LOG(__PRETTY_FUNCTION__);
ptrContext->template IMessageContext<T>::attachListener(ptrListener);
ptrListener->template IMessageListener<T>::attachContext(ptrContext);
}
template <typename T>
void detach(IMessageContext<T>* ptrContext, IMessageListener<T>* ptrListener)
{
LOG(__PRETTY_FUNCTION__);
ptrContext->template IMessageContext<T>::detachListener(ptrListener);
ptrListener->template IMessageListener<T>::detachContext(ptrContext);
}
} //namespace messaging
#endif // MESSAGING_HPP
And an example of usage:
#include <iostream>
#include "messaging.hpp"
#include <string>
using namespace std;
class context : public messaging::IMessageContext<int>,
public messaging::IMessageContext<std::string>
{
public:
void go(const int &i)
{
messaging::send<int>(this, i);
}
void go(const string &s)
{
messaging::send<std::string>(this, s);
}
};
class listener : public messaging::IMessageListener<int>,
public messaging::IMessageListener<string>
{
virtual void handleMessage(const int &i){
std::cout << i << std::endl;
}
virtual void handleMessage(const string &i) {
std::cout << i << std::endl;
}
public:
virtual ~listener(){}
};
int main()
{
context c;
listener l;
messaging::attach<int>(&c,&l);
messaging::attach<std::string>(&c,&l);
c.go(1234);
c.go("this is a string message");
return 0;
}
As I am still new to C++ and programming in general, I was hoping to find critique on method and style, as well as thoughts on usefulness and efficiency of the idea.