Решил тут для себя разобраться в реализации сигнально-слотового механизма, основаного на шаблонах.
Выкладываю то что успел накатать (описание ниже):
файл signal_slot.h
C++ (Qt)
#ifndef SIGNAL_SLOT_H
#define SIGNAL_SLOT_H
#include <list>
template <class T_receiver, class T_return, class T_arg>
class Slot
{
public:
typedef T_return (T_receiver::*function_type_1)(T_arg);
typedef T_return (T_receiver::*function_type_2)(const T_arg &);
typedef T_return (T_receiver::*function_type_3)(T_arg) const;
typedef T_return (T_receiver::*function_type_4)(const T_arg &) const;
Slot(T_receiver *obj, function_type_1 slot);
Slot(T_receiver *obj, function_type_2 slot);
Slot(T_receiver *obj, function_type_3 slot);
Slot(T_receiver *obj, function_type_4 slot);
~Slot();
T_return operator() (const T_arg &arg);
private:
Slot();
function_type_1 _slot_1;
function_type_2 _slot_2;
function_type_3 _slot_3;
function_type_4 _slot_4;
T_receiver *_obj;
int _index;
};
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot()
{
}
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot(T_receiver *obj, function_type_1 slot)
{
_obj = obj;
_slot_1 = slot;
_index = 1;
}
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot(T_receiver *obj, function_type_2 slot)
{
_obj = obj;
_slot_2 = slot;
_index = 2;
}
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot(T_receiver *obj, function_type_3 slot)
{
_obj = obj;
_slot_3 = slot;
_index = 3;
}
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot(T_receiver *obj, function_type_4 slot)
{
_obj = obj;
_slot_4 = slot;
_index = 4;
}
template <class T_receiver, class T_return, class T_arg>
inline T_return Slot<T_receiver, T_return, T_arg>::operator ()(const T_arg &arg)
{
if (_index == 1)
return (_obj->*_slot_1)(arg);
if (_index == 2)
return (_obj->*_slot_2)(arg);
if (_index == 3)
return (_obj->*_slot_3)(arg);
return (_obj->*_slot_4)(arg);
}
template <class T_receiver, class T_return, class T_arg>
class Signal
{
public:
typedef T_return (T_receiver::*function_type_1)(T_arg);
typedef T_return (T_receiver::*function_type_2)(const T_arg &);
typedef T_return (T_receiver::*function_type_3)(T_arg) const;
typedef T_return (T_receiver::*function_type_4)(const T_arg &) const;
Signal();
~Signal();
void operator()(const T_arg &arg);
void connect(T_receiver &obj, function_type_1 slot);
void connect(T_receiver &obj, function_type_2 slot);
void connect(T_receiver &obj, function_type_3 slot);
void connect(T_receiver &obj, function_type_4 slot);
private:
std::list<Slot<T_receiver, T_return, T_arg>* > _list;
typedef typename std::list<Slot<T_receiver, T_return, T_arg>* >::iterator _iterator;
};
template <class T_receiver, class T_return, class T_arg>
inline Signal<T_receiver, T_return, T_arg>::Signal()
{
}
template <class T_receiver, class T_return, class T_arg>
inline Signal<T_receiver, T_return, T_arg>::~Signal()
{
_list.clear();
}
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::connect(T_receiver &obj, function_type_1 slot)
{
_list.push_back(new Slot<T_receiver, T_return, T_arg>(&obj, slot));
}
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::connect(T_receiver &obj, function_type_2 slot)
{
_list.push_back(new Slot<T_receiver, T_return, T_arg>(&obj, slot));
}
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::connect(T_receiver &obj, function_type_3 slot)
{
_list.push_back(new Slot<T_receiver, T_return, T_arg>(&obj, slot));
}
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::connect(T_receiver &obj, function_type_4 slot)
{
_list.push_back(new Slot<T_receiver, T_return, T_arg>(&obj, slot));
}
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::operator ()(const T_arg &arg)
{
for (_iterator it = _list.begin(); it != _list.end(); it++)
(*it)->operator()(arg);
}
#endif // SIGNAL_SLOT_H
Краткое описание.
Во-первых Сигнал имеет только один передаваемый аргумент (я не думаю что это сильно ущербно, поскольку если требуется передать более одного аргумента, всегда можно создать структуру или класс и объект его уже передавать)
Соответственно слот, также должен иметь только один аргумент.
Далее
Класса Slot
Он фактически представляет из себя функторный класс, который биндит указатель на метод класса, который (метод) будет реализовывать слот.
Этот класс пользователь в своём коде явно нигде не использует. Он введён для удобства и используется классом Signal
Класс Signal объявлен как
C++ (Qt)
template <class T_receiver, class T_return, class T_arg> class Signal
где
T_receiver - это класс получатель
T_return - тип возвращаемого аргумента функции (слота)
T_arg - тип аргумента (слота и сигнала, соответственно)
Используется это так:
файл main.cpp
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
template <class T>
class Sender
{
public:
Sender() {}
Signal<T, void, std::string> signal;
void run() {
signal("Hello word!");
}
};
class Receiver
{
public:
Receiver(){}
void mySlot1(const std::string &str) {
std::cout << "mySlot1 received a signal: " << str << std::endl;
}
void mySlot2(std::string str) {
std::cout << "mySlot2 receiver a signal: " << str << std::endl;
}
};
int main()
{
Sender<Receiver> sender;
Receiver receiver;
sender.signal.connect(receiver, &Receiver::mySlot1);
sender.signal.connect(receiver, &Receiver::mySlot2);
sender.run();
return 0;
}
Недостатком такого подхода является то, что общатся между собой могут лишь объекты тех классов, что указаны (в параметрах шаблона) при их создании. Т.е. в данном случае, объекты класса Receiver могут получать сигналы от объектов класса Sender. Но объекты класса Sender не могут слать сигналы объектам другого класса, если при определении класа Sender не указать дополнительный параметр шаблона и соответствующий сигнал с этим параметром.
Я пока не раскурил, как эта проблема решена, например в boost или в libsigc++
Если кто подскажет, скажу спасибо)
Далее хотелось бы реализовать метод disconnect, а также при коннекте проверять, есть ли в списке зарегистрированных слотов тот, который коннектится. Сейчас, если дважды подрят вызвать connect то в список будет содержать два одинаковых слота, что не есть гут.
Как это сделать пока думаю над этим..
Ещё такой вопрос: кто знает как присвоить указатель на метод одного класса к указателю на метод другого класса? И хорошо ли это?
В общем делаю это, в первую очередь, чтоб разобраться со всем этим делом))
архив с проектом прикреплён