I've been looking at a few signal/slot implementations, and with no exception they were pretty complicated, some even relying on MOC and extra code generation, like those of Qt.
I realize there are concerns such as threat safety and whatnot, but for a simple, single threaded scenario, is there something wrong with going for a simple approach, something like:
typedef void (*fPtr)();
class GenericButton
{
public:
GenericButton() : funcitonToCall(nullptr) {}
void setTarget(fPtr target) {
funcitonToCall = target;
}
void pressButton() {
if (funcitonToCall) funcitonToCall();
}
private:
fPtr funcitonToCall;
};
void doSomething(){
std::cout << "doing something..." << std::endl;
}
void doSomethingElse(){
std::cout << "doing something else..." << std::endl;
}
int main(){
GenericButton myButton;
myButton.setTarget(doSomething);
myButton.pressButton();
myButton.setTarget(doSomethingElse);
myButton.pressButton();
}
It is still possible to chain several other methods and pass data in the target void function. So why all the complexity for something as trivial as executing some code when a button gets clicked.
This is a perfectly sensible solution, but don't restrict yourself to just function pointers. Use std::function
which allows you to bind things, call member functions on objects, use lambdas and still resort to a function pointer where it makes sense. Example:
#include <iostream>
#include <functional>
using namespace std::placeholders;
class GenericButton
{
public:
typedef std::function<void()> fPtr;
GenericButton() : funcitonToCall(nullptr) {}
void setTarget(fPtr target) {
funcitonToCall = target;
}
void pressButton() {
if (funcitonToCall) funcitonToCall();
}
private:
fPtr funcitonToCall;
};
struct foo {
void doSomething() const {
std::cout << "doing something in a foo..." << std::endl;
}
static void alternative(int i) {
std::cout << "And another, i=" << i << "\n";
}
};
void doSomethingElse() {
std::cout << "doing something else..." << std::endl;
}
int main() {
GenericButton myButton;
foo f;
myButton.setTarget(std::bind(&foo::doSomething, &f));
myButton.pressButton();
myButton.setTarget(doSomethingElse);
myButton.pressButton();
myButton.setTarget(std::bind(foo::alternative, 666));
myButton.pressButton();
myButton.setTarget([](){ std::cout << "Lambda!\n"; });
myButton.pressButton();
}
There's almost always a better solution in C++ than function pointers.
If you don't have std::function
/std::bind
there's always alternatives in boost that work and you can roll your own std::function
alternative without too much work which would be worth doing if you want to make something like this.
Most of the signal/slot mechanisms that are around date from a time when things like boost::bind
was not a viable option. Those days are long gone and you can get something standard and more flexible for little more complexity than just a function pointer.