Я работаю над кодом для реализации облегченной версии чего-то похожего на механизм сигналов / слотов Qt (т.е. прославленный шаблон наблюдателя), где я могу соединять «сигналы» с «слотами». «Соединение» может быть выполнено только в том случае, если две подписи идентичны. Каждый раз, когда испускается «сигнал», все вызовы любых подключенных «слотов» будут помещены в очередь для выполнения в соответствующих потоках в более позднее время.
Я провел большое количество исследований на это topi c и понимаю, что то, что я хочу, может быть достигнуто с помощью некоторой комбинации функторов и шаблонов. Однако я не могу понять, как заставить все работать так, как мне хотелось бы. Кроме того, это используется во встроенном процессоре, поэтому я не хочу использовать std :: function, который, как я читал, связан с большим объемом накладных расходов.
Пока что у меня есть написал успешную подпись для функции "подключения" следующим образом:
template<typename OBJECT, typename FUNC>
static void connect(OBJECT *sender, FUNC signal, OBJECT *receiver, FUNC slot) {
}
//...
Test1 t;
Test1::connect(&t, &Test1::signal1, &t, &Test1::slot1);
Теперь мне нужен способ сохранить вызов функции, связанный с объектом / слотом, который будет сохранен и вызван сигналом, когда он испускается. Я понимаю, что это нужно делать с помощью Functor. Однако я не могу понять, как написать функтор, который является c объектом, но требует специальной c подписи. Я ищу что-то вроде:
GenericFunctor<int, int> slotsConnectedToSignal1[4];
GenericFunctor<int, char, int> slotsConnectedToSignal2[4];
Таким образом, сигнал (который имеет ту же сигнатуру, что и массив, содержащий его подключенные слоты) может l oop через массив и вызывать все функтор.
Есть ли способ достичь того, что я пытаюсь выполнить sh и на правильном ли я пути?
Спасибо!
РЕДАКТИРОВАТЬ
Я приближаюсь к тому, что хочу, используя следующие определения для connect ().
template <typename ObjSender, typename Ret, typename ObjReceiver>
static void connect(ObjSender *sender, Ret(ObjSender::*signal)(), ObjReceiver *receiver, Ret(ObjReceiver::*slot)()) {
std::function<Ret()> fSender = std::bind(signal, sender);
std::function<Ret()> fReceiver = std::bind(slot, receiver);
}
template <typename ObjSender, typename Ret, typename ARG0, typename ObjReceiver>
static void connect(ObjSender *sender, Ret(ObjSender::*signal)(ARG0), ObjReceiver *receiver, Ret(ObjReceiver::*slot)(ARG0)) {
std::function<Ret(ARG0)> fSender = std::bind(signal, sender, std::placeholders::_1);
std::function<Ret(ARG0)> fReceiver = std::bind(slot, receiver, std::placeholders::_1);
}
Теперь мой следующий вопрос - как сохранить и вызвать эти std :: функциональные объекты в их правильных сигналах. Например, когда пользователь вызывает signal1 (1, 2), эта функция должна иметь возможность искать все связанные с ней «связанные» объекты std :: function и вызывать каждый из них по очереди с аргументами.
Кроме того, я должен упомянуть, что этот код предназначен для встроенной системы, поэтому я пытаюсь разработать его с нуля, чтобы минимизировать накладные расходы из внешних библиотек.
EDIT 2
Основываясь на некоторых полученных мной отзывах, это моя последняя попытка достичь желаемых результатов.
template<typename ... ARGS>
class Signal {
public:
void operator()(ARGS... args) {
_connection(args...);
}
void connect(std::function<void(ARGS...)> slot) {
_connection = slot;
}
private:
std::function<void(ARGS...)> _connection;
};
class Test2 {
public:
Signal<int, int> signal1;
Signal<int, int> signal2;
void slot1(int a, int b) {
signal1(a, b);
}
void slot2(int c, int d) {
int i = c + d;
(void)i;
}
};
int main(void) {
Test2 t2;
t2.signal1.connect(t2.signal2);
t2.signal2.connect(std::bind(&Test2::slot2, &t2, std::placeholders::_1, std::placeholders::_2));
t2.slot1(1, 2);
}
Однако в этом случае у меня все еще есть проблема: когда я хочу подключиться к функции «слот» (вместо другого сигнала), мне нужно использовать std :: bind с нужным количеством заполнителей. Я знаю, что должен быть способ сделать это, но я недостаточно знаком с std :: function и лямбда-выражениями.