Sunday, 22 July 2012

Using boost::signal Example

From my previous post you maybe aware I had a problem integrating two different modules, produced separately and in isolation, which when brought together instigated a compile time error... Well, over the day I've spent a few hours... well about four... trying to work out what to do...

I had thought I needed some mechanism to be connectionless - hence my use of boost::asio for its UDP operation - which could deliver the messages with as little programmer (or rather code dependence on the programmer) as possible.

I spent sometime looking at message exchange via some web technology, looked at named pipes, shared memory, and even gave up on the connectionless ideal and went for a network topology of TCP/IP Connections... None came up as acceptable to my needs, they were either so complex in code as to need very significant amounts of testing, or they brought into my code base so many external files as to swamp the project and blur the line between multiple licenses.

However, late in the day I stumbled; and I feel a fool for stumbling so late; over boost::signals.  Initially I ran through their simple tutorial and was happy to see a function being invoked after connection from another location.

After a little thinking I also stumbled over a pattern of thinking via which I was able to replace my asio use of UDP as a message transporter to send a message to multiple locations through my code.


My pattern is to have one class control a copy of the signal, and to pass pointers to this to each outlying class, these outlying classes can then connect to receive the signal into local functions bound with boost::bind and viola... A signal sent to the central, and only, copy of the signal can be propagated extremely quickly throughout the other classes.

Here is my code:


#include <iostream>
#include <string>

#include <boost/signal.hpp>
#include <boost/bind.hpp>


using namespace std;

typedef boost::signal<void(const string&)> Func;
typedef Func* FuncPtr;

class A
{
private:

FuncPtr m_Signal;

boost::signals::connection m_Connection;

void OnSignal (const string& p_Message)
{
cout << "A::OnSignal [" << p_Message << "]" << endl;
}


public:

A (FuncPtr p_Signal)
:
m_Signal (p_Signal)
{
m_Connection = m_Signal->connect(boost::bind(&A::OnSignal, this, _1));
}

~A ()
{
m_Signal->disconnect(m_Connection);
m_Signal = nullptr;
}

};

class B
{
private:

Func m_Signal;

public:

B ()
{
}

FuncPtr Getsignal()
{
return &m_Signal;
}

void Transmit (const string& p_Message)
{
m_Signal(p_Message);
}

};


int main ()
{
B* b = new B();
A* a = new A(b->Getsignal());

b->Transmit("Hello World");

delete a;
delete b;

cin.get();
return 0;
}


This easily provides a simple framework upon which I can wrap a single instance of a signal, or different signals into a single class, in this example "B" and then pass out the pointer to that instance, in this example to "A".  And then the transmit is sent to all listeners.

One can register multiple instances of the "A" class and the transmit of the call goes to all the handlers for it.

I believe I may be able to turn what I had represented as an Event raised from many places, into a single Signal instantiated in one location.

I admit I will end up with multiple signals, one for each Event I had before, but the transmission system drops to zero lines of code, and all the lines of code I am using in the boost library have been very very well tested, unlike the code one would employ from ones own hand...

My new task therefore is to turn this reverse concept of a message to better meet my implementation of a state machine.

No comments:

Post a Comment