I put up some code on github that implements basic signals and slots functionality using standards compliant C++. It’s one header file “signals.hpp”. See here: link to github
This implementation of signals is a re-work of some code by a user _pi on the forums for the cross-platform application framework Juce — that thread is here — which was itself a re-work of some example code posted by Aardvajk in the GameDev.net forums (here). I just put it all together, cleaned things up, fixed some bugs, and added lambda support.
The basic idea is that given variadic templates being added to C++ it became possible for a more straight-forward implementation of signal and slot functionality beyond what was done for boost::signals et. al. — that is what Aardvajk’s original article was about. _pi changed Aardvajk’s code to make it such that slots are a thing you inherit from, which makes more sense to me. I changed _pi’s code so that it uses std::functions to store the handlers thus allowing lambdas with captures to be attached to a signal.
Usage is like the following:
#include "signals.hpp"
#include <iostream>
// a handler is a kind of slot, that, as an implementation detail, requires usage of the
// "curiously recurring template pattern". That is, the intention is for instances of
// a class C that will react to a signal firing to have an is-a relationship with a slot
// parametrized on class C itself.
class CharacterHandler : public Slot<CharacterHandler>
{
public:
void HandleCharacter(char c)
{
std::cout << "The user entered '" << c << "'" << std::endl;
}
};
class DigitHandler : public Slot<DigitHandler>
{
public:
void HandleCharacter(char c)
{
if (c >= '0' && c <= '9') {
int n = static_cast<int>(c - '0');
std::cout << " " << n << " * " << n << " = " << n*n << std::endl;
}
}
};
int main()
{
bool done = false;
char c;
Signal<char> signal;
CharacterHandler character_handler;
DigitHandler digit_handler;
// can attach a signal to a slot with matching arguments
signal.connect(character_handler, &CharacterHandler::HandleCharacter);
// can also attach a lambda, associated with a slot.
// (the lambda could capture the slot and use it like anything else it captures
// however technically all the associated slot is doing is allowing you to have
// a way of disconnecting the lambda e.g. in this case signal.disconnect(characterHandler)
signal.connect(character_handler,
[&](char c) -> void {
if (c == 'q')
done = true;
}
);
// can also attach a slot to a signal ... this means the same as the above.
digit_handler.connect(signal, &DigitHandler::HandleCharacter);
do
{
std::cin >> c;
signal.fire(c);
} while (! done);
// can disconnect like this
character_handler.disconnect(signal);
// or this
signal.disconnect(digit_handler);
//although disconnecting wasnt necessary here in that just gaving everything go out of scope
// wouldve done the right thing.
return 0;
}