Я смотрел на несколько реализаций сигнала / слота, и без исключения они были довольно сложными, некоторые даже полагались на MOC и дополнительную генерацию кода, как в Qt.
Я понимаю, что есть проблемы, такие как безопасность угроз и еще много чего, но для простого однопоточного сценария есть ли что-то не так с переходом на простой подход, что-то вроде:
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();
}
Все еще возможно связать несколько других методов и передать данные в целевую пустую функцию. Так почему вся сложность для чего-то столь же тривиального, как выполнение некоторого кода при нажатии кнопки.
Это вполне разумное решение, но не ограничивайте себя только указателями функций. использование std::function
который позволяет вам связывать вещи, вызывать функции-члены на объектах, использовать лямбды и при этом прибегать к указателю на функцию там, где это имеет смысл. Пример:
#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();
}
Почти всегда лучшее решение в C ++, чем указатели на функции.
Если у вас нет std::function
/std::bind
всегда есть альтернативы в ускорении этой работы, и вы можете свернуть свой собственный std::function
Альтернатива без лишней работы, которую стоило бы сделать, если вы хотите сделать что-то подобное.
Большинство механизмов сигналов / слотов, которые существуют около того времени, когда такие вещи, как boost::bind
не был жизнеспособным вариантом. Эти дни давно прошли, и вы можете получить что-то стандартное и более гибкое для чуть большей сложности, чем просто указатель на функцию.
Других решений пока нет …