There are two syntaxes to declare a signal: normal syntax and compatibility syntax.
The normal syntax:
signal< void () > mySignal;
signal< void ( ArgumentType1, ArgumentType2, ... ) > mySignal;
The compatibility syntax:
signal_0 mySignal0;
signal_1< ArgumentType1 > mySignal1;
signal_2< ArgumentType1, ArgumentType2 > mySignal2;
...
Your compiler must support partial template specialization to be able to compile the normal syntax. Use the compatibility syntax otherwise.
Now, we proceed to a simple example that shows the basic usage of the library. Suppose you have the following classes:
class Console { public: void onPrintError ( const std::string& msg ); void onPrintWarning ( const std::string& msg ); // ... }; class Compiler { public: void compile ( const std::string& sourcePath ); signal< void ( const std::string& ) > sigError; signal< void ( const std::string& ) > sigWarning; // ... }; void Compiler::compile ( const std::string& sourcePath ) { // ... std::string strError = ...; // generate error text sigError ( strError ); std::string strWarning = ...; // generate warning text sigWarning ( strWarning ); // ... }
Console console;
Compiler compiler;
compiler.sigError.connect< Console, & Console::onPrintError >( console );
compiler.sigWarning.connect< Console, & Console::onPrintWarning >( console );
compiler.compile();
Now the Console will receive onPrintError and onPrintWarning method calls.
Compiler compiler;
Console console;
then we'd have a problem, with dangling connections from the compiler object to the already deleted console object. To exclude this possibility we can use explicit disconnect feature. Here we have three choices:
The first one is to derive Console from msignals::target base class:
class Console : public target { // ... };
Now we can use the following construct to remove the connection to the console object:
compiler.sigError.disconnect ( & console );
In this case it's safe to pass a pointer to any class derived from Console.
What about the situation when we can't modify the inheritance hierarchy? We can still use the above syntax, but we must be aware of the fact that the pointer will be converted to void* and compared by value. This means that we must pass a pointer to the Console class, but not any derived class (since the connection won't be removed in that case). Using disconnect without deriving from target is the second possibility. It's somewhat dangerous, so be careful.
The third possibility is to use a connection handle:
connection conErr =
compiler.sigError.connect< Console, & Console::onPrintError >( console );
connection conWarn =
compiler.sigWarning.connect< Console, & Console::onPrintWarning >( console );
// ...
disconnect ( conErr );
disconnect ( conWarn );
Here we use disconnect as a free function, not a method. Since the connection does remember both signal and slot, we don't need to specify the signal.
We can also remove all connections from particular signal:
compiler.sigError.disconnect_all();
managed_target base class instead of plain target:
class Console : public managed_target { // ... };
Now we don't need to do any bookkeeping, as all connections established to the Console slots will be automatically removed just before the object is deleted.
struct ILogFile { virtual void onPrintError ( const std::string& msg ) = 0; virtual void onPrintWarning ( const std::string& msg ) = 0; // ... };
Given the Console instance and ILogFile interface pointer, we can connect them both:
Console console;
Compiler compiler;
ILogFile* pLogFile = ...;
compiler.sigError.connect< Console, & Console::onPrintError >( console );
compiler.sigWarning.connect< Console, & Console::onPrintWarning >( console );
compiler.sigError.connect< ILogFile, & ILogFile::onPrintError >( *pLogFile );
compiler.sigWarning.connect< ILogFile, & ILogFile::onPrintWarning >( *pLogFile );
compiler.compile();
Here the Console functions will be called before the ILogFile ones. But what if we want the reverse order (first ILogFile, then Console)? We can use another form of connect:
compiler.sigError.connect_first< ILogFile, & ILogFile::onPrintError >( *pLogFile );
compiler.sigWarning.connect_first< ILogFile, & ILogFile::onPrintWarning >( *pLogFile );
Now the ILogFile connection will be added at the beginning, and processed first.
1.5.5