signal_link Class Template Reference
[Utility]

#include <signal_link.hpp>

Inheritance diagram for signal_link:

Inheritance graph
[legend]
List of all members.

Detailed Description

template<typename Descendant, typename Signature>
class ame::signal_link< Descendant, Signature >

Provides easy linking of components through Boost signals.

Parameters:
Descendant The name of the class subclassing signal_link.
Signature The signature of the signal being sent out.
Use this class as a base class for classes that produce a signal of a particular signature. Classes that contain operator() with the same signature will be able to receive those signals. Multiple objects can be connected to receive the same signal.

Signals are sent using the Boost.Signals (http://www.boost.org/doc/html/signals.html) You can view the associated tutorial (http://www.boost.org/doc/html/signals/tutorial.html) to get familiar with how it works. Using signal_link as a base class for signal sending, signals can be received by any class using the operator() of the appropriate signature. Such receivers are called slots.

Examples

Simple connections

For example, consider the signature "void ()". This is the signature of a function that returns void and takes no arguments. A class that can send out signals of such a signature would be defined as follows:

class SignalVoid : public signal_link<SignalVoid, void ()>
{
public:
        void Bang()
        {
                out(); // send out the void() signal when Bang is called
        }
}; // end class SignalVoid

Signals of the same signature could be received by the following class:

class SignalVoidCounter : public boost::signals::trackable
{
        int cnt;
public:
        SignalVoidCounter() : cnt(0) {}
        void operator()()
        {
                cnt++; // whenever a void() signal is received, increase the counter
        }
        int GetCount()
        {
                return cnt;
        }
}; // end class SignalVoidCounter

The following demonstrates how you would make a signal sender / receiver where the signal carried a float argument:

class SignalFloat : public signal_link<SignalFloat, void (float)>
{
        float val;
public:
        SignalFloat(float val) : val(val) {}
        void operator()()
        {
                out(val); // upon receiving a void() signal, send out the stored value
        }
}; // end class SignalFloat

class SignalFloatCollector : public boost::signals::trackable
{
        optional<float> last;
public:
        void operator()(float x)
        {
                last = x; // store the received value of the void(float) signal
        }
        optional<float> GetLast()
        {
                return last;
        }
}; // end class SignalFloatCollector

The SignalFloat class above can also receive signals of signature void(), because it contains a void operator()() definition.

Using the above classes, it is easy to connect them together using the signal_link::operator>>=:

void simple_test()
{
        SignalVoid banger;
        SignalVoidCounter counter;

        banger >>= counter; // this connects banger to counter

        banger.Bang(); // banger will now output a signal, and
        BOOST_CHECK(counter.GetCount() == 1); // counter will count it
        
        SignalFloat floater(2.5f);
        SignalFloatCollector collector;

        banger >>= floater >>= collector; // banger is now also connected to floater

        banger.Bang(); // signal from banger will now
        BOOST_CHECK(counter.GetCount() == 2); // increase the counter count
        BOOST_CHECK(collector.GetLast() == optional<float>(2.5f)); // and cause floater to output 2.5
} // end void simple_test()

Branching connections

More complex connections can also be made relatively easily using both signal_link::operator>>= and signal_link::operator>=. The following code will result in the same final signal network as the Simple connections example above:

void branching_test()
{
        SignalVoid banger;
        SignalVoidCounter counter;
        SignalFloat floater(2.5f);
        SignalFloatCollector collector;
        
        banger
                >= (floater >>= collector) // floater connects to collector, banger to floater
                >= counter; // and banger to counter
                
        banger.Bang();
        BOOST_CHECK(counter.GetCount() == 1);
        BOOST_CHECK(collector.GetLast() == optional<float>(2.5f));
} // end void branching_test()

Disconnecting connections

Connections can be terminated in two ways. One is through the "trackable" mechanism of Boost.Signals, which will automatically destroy connections to a trackable object when the object is destroyed. The other way is through the disconnect_all_slots method of the signal sender.

void disconnect_test()
{
        SignalVoid banger;
        {
                SignalVoidCounter counter;
                SignalFloat floater(2.5f);
                SignalFloatCollector collector;

                banger
                        >= counter
                        >= (floater >>= collector);

                banger.Bang();
                BOOST_CHECK(counter.GetCount() == 1);
                BOOST_CHECK(collector.GetLast() == optional<float>(2.5f));
        } // counter, floater, and collector are now gone and disconnected
        BOOST_CHECK(banger.default_signal().num_slots() == 0); 

        SignalVoidCounter counter;

        banger >>= counter;
        banger.disconnect_all_slots();

        banger.Bang();
        BOOST_CHECK(counter.GetCount() == 0);
} // end void disconnect_test

Multiple inputs of different signatures

It is simple to have an object provide multiple slots through operator() functions of different signatures. The following class does so through providing 1-ary slots of different types:

class SignalIntFloatCollector : public boost::signals::trackable
{
        optional<int> last_int;
        optional<float> last_float;
public:
        void operator()(int x)
        {
                last_int = x;
        }
        void operator()(float x)
        {
                last_float = x;
        }
        optional<int> GetLastInt()
        {
                return last_int;
        }
        optional<float> GetLastFloat()
        {
                return last_float;
        }
}; // end class SignalIntFloatCollector

The following class, on the other hand, uses slots of different number of arguments:

class SignalMultiCollector : public boost::signals::trackable
{
        optional<float> last, last1, last2;
        int cnt;
public:
        SignalMultiCollector() : cnt(0) {}
        void operator()()
        {
                cnt++;
        }
        int GetCount()
        {
                return cnt;
        }
        void operator()(float val1, float val2)
        {
                last1 = val1;
                last2 = val2;
        }
        optional<float> GetLast1()
        {
                return last1;
        }
        optional<float> GetLast2()
        {
                return last2;
        }
        void operator()(float x)
        {
                last = x;
        }
        optional<float> GetLast()
        {
                return last;
        }
}; // end class SignalMultiCollector

In such cases, where the operator() functions differ in their signature, standard connection operators will work out the correct connection:

void multi_type_test()
{
        SignalVoid banger;
        SignalInt inter(2);
        SignalFloat floater(3.3f);
        SignalIntFloatCollector collector;

        banger
                >= (inter >>= collector)
                >= (floater >>= collector);

        banger.Bang();
        BOOST_CHECK(collector.GetLastInt() == optional<int>(2));
        BOOST_CHECK(collector.GetLastFloat() == optional<float>(3.3f));
} // end void multi_type_test()

Multiple inputs of the same signature

In some cases, a class may want to receive multiple signals of the same signature. For example, the following class can receive a void() signal through its inherited operator() function, as well as through the operator() function of member "other":
class Signal2VoidCounter : public SignalVoidCounter
{
public:
        SignalVoidCounter other;
}; // end class Signal2VoidCounter

Similarly, the following class could receive void() signals both through operator() and through AltInput:

class Signal2VoidInputs : public signal_link<Signal2VoidInputs, void(int)>
{
        int result;
public:
        Signal2VoidInputs() : result(0) {};
        void operator()()
        {
                result++;
                out(result);
        }
        void AltInput()
        {
                result+=10;
                out(result);
        }
        int GetResult()
        {
                return result;
        }
}; // end class Signal2VoidInputs

The following example shows how to connect signals to all of the above slots. For the class Signal2VoidInputs, this is accomplished using the slot_selector function:

void multi_in_test()
{
        SignalVoid banger;
        Signal2VoidCounter counter;
        
        banger
                >= counter
                >= counter.other;
        
        banger.Bang();
        BOOST_CHECK(counter.GetCount() == 1);
        BOOST_CHECK(counter.other.GetCount() == 1);

        Signal2VoidInputs inputs;

        banger
                >= inputs
                >= slot_selector<void ()> (inputs, &Signal2VoidInputs::AltInput);

        banger.Bang();
        BOOST_CHECK(inputs.GetResult() == 11);
}; // end void multi_in_test


Public Types

typedef boost::signal< Signature > default_signal_t

Public Member Functions

 signal_link (const signal_link &)
const default_signal_t & default_signal () const
 Returns the default out signal.
template<typename T>
Descendant & operator>>= (T &link)
 Connects a sequence of components using signals.
template<typename T>
Descendant & operator>= (T &link)
 Allows branching in a component connection sequence.
template<typename T>
Descendant & operator>>= (slot_selector_t< T, Signature > link)
 Allows slot functions other than operator() to be used in a sequence of components.
template<typename T>
Descendant & operator>= (const slot_selector_t< T, Signature > &link)
 Allows slot functions other than operator() to be used with branching.
void disconnect_all_slots ()
 Disconnects all slots connected to the signal_link.

Protected Attributes

default_signal_t out


Member Function Documentation

Descendant& operator>>= ( T &  link  )  [inline]

Connects a sequence of components using signals.

This operator is identical to signal_link::operator>= (it connects the left component to the right component, and returns a reference to the left component), except it is evaluated right to left. This makes it semantics more suitable for connecting a chain of connections.

Descendant& operator>= ( T &  link  )  [inline]

Allows branching in a component connection sequence.

This operator is identical to signal_link::operator>>=, (it connects the left component to the right component, and returns a reference to the left component) except it is evaluated left to right. This makes its semantics more suitable for branching connections.

Descendant& operator>>= ( slot_selector_t< T, Signature >  link  )  [inline]

Allows slot functions other than operator() to be used in a sequence of components.

See also:
slot_selector()

Descendant& operator>= ( const slot_selector_t< T, Signature > &  link  )  [inline]

Allows slot functions other than operator() to be used with branching.

See also:
slot_selector()


The documentation for this class was generated from the following file:
Generated on Tue Mar 6 17:57:23 2007 for AME Repository by  doxygen 1.5.1-p1