Signal和Slot机制
其实qt提供的对象间通讯机制。
实现细节
思路
实现类似与信号与槽的机制,无非就是做一些薄记工作,qt中通过虚函数和moc来实现。那我们怎么实现?我的思路是signal是一个对象,其他的slot将自己的回调注册进signal对象中,signal完成薄记工作。那么需要实现的细节归于一下几点。
1. 参数的提取与匹配。
2. slot容器的制作。
3. slot的调用。
参数处理
怎么处理参数是一大难题。我们必须推导出各类型,包括类的类型和参数类型,在此我们引入很多特性模板类。
template <typename T>
struct TypeTraits
{
typedef T type_value;
typedef T& refType_value;
typedef const T& constRefType_value;
typedef T* pointerType_value;
typedef const T* constPointerType_value;
};
//>>>
template <typename Re>
struct ParameterTraits
{
static const int size = -1;
};
template <typename Re>
struct ParameterTraits<Re (*)()>
{
static const int size = 0;
};
template <typename Re, typename T0>
struct ParameterTraits<Re (*)(T0)>
{
static const int size = 1;
typedef T0 P0;
};
.......
//>>>>
template <typename Class, typename Re>
struct ParameterTraits<Re (Class::*)()>
{
static const int size = 0;
typedef Class classType_value;
};
template <typename Class, typename Re, typename T0>
struct ParameterTraits<Re (Class::*)(T0)>
{
static const int size = 1;
typedef T0 P0;
typedef Class classType_value;
};
template <typename Class, typename Re, typename T0, typename T1>
struct ParameterTraits<Re (Class::*)(T0, T1)>
{
static const int size = 2;
typedef T0 P0;
typedef T1 P1;
typedef Class classType_value;
};
上述模板类作用在于提取c-style function和c++类成员函数的类类型和参数类型。
//>>>
template<int Size, typename T>
struct ParameterTupleTraitsProxy
{
};
template<typename T>
struct ParameterTupleTraitsProxy<0, T>
{
typedef std::tuple<> Parameters;
};
template<typename T>
struct ParameterTupleTraitsProxy<1, T>
{
typedef std::tuple<typename T::P0> Parameters;
};
template<typename T>
struct ParameterTupleTraitsProxy<2, T>
{
typedef std::tuple<typename T::P0, typename T::P1> Parameters;
};
........
template <typename ParameterTraits>
struct ParameterTupleTraits
{
typedef typename ParameterTupleTraitsProxy<ParameterTraits::size, ParameterTraits>::Parameters Parameters;
};
上述用来保存signal发送时,参数的保存,传递到slot处的模板类,使用了c++0x的Tuple。
slot
template <typename Paras>
struct SlotBase
{
virtual ~SlotBase() = 0;
virtual void dotask(Paras paras) = 0;
};
template <typename Paras>
SlotBase<Paras>::~SlotBase()
{
}
template <typename M>
struct Slot:public SlotBase<typename ParameterTupleTraits<ParameterTraits<M> >::Parameters>
{
typedef typename TypeTraits<typename ParameterTraits<M>::classType_value>::pointerType_value type_value;
typedef typename ParameterTupleTraits<ParameterTraits<M> >::Parameters Parameters;
Slot(type_value sloter, M method):m_object(sloter), m_method(method)
{
}
void dotask(Parameters paras)
{
impleDoTask(m_object, m_method, paras);
}
type_value m_object;
M m_method;
};
SlotBase为做容器的基类,dotask
函数完成实际的函数调用。Slot保存调用对象和成员函数指针,实现实际的调用,但是参数个数是不同的,这个需要引入对个个参数调用的特例。如下:
template <typename Class, typename Method>
void impleDoTask(Class *c, Method m, std::tuple<> p)
{
(c->*m)();
}
template <typename Class, typename Method, typename T0>
void impleDoTask(Class *c, Method m, std::tuple<T0> p)
{
(c->*m)(std::get<0>(p));
}
template <typename Class, typename Method, typename T0, typename T1>
void impleDoTask(Class *c, Method m, std::tuple<T0, T1> p)
{
(c->*m)(std::get<0>(p), std::get<1>(p));
}
template <typename Class, typename Method, typename T0, typename T1, typename T2>
void impleDoTask(Class *c, Method m, std::tuple<T0, T1, T2> p)
{
(c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p));
}
template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3>
void impleDoTask(Class *c, Method m, std::tuple<T0, T1, T2, T3> p)
{
(c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p));
}
其对不同个数参数提供具体的模板特化。
Signal
template <typename Method>
struct Signal
{
~Signal()
{
for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
{
delete (*ite);
}
sloters.clear();
}
template <typename M>
bool connect(typename Slot<M>::type_value object, M method)
{
typename listValue_type::iterator ite = std::find_if(sloters.begin(), sloters.end(), FindHelper<M>(object, method));
if (ite != sloters.end()) {
return false;
}
sloters.push_back(new Slot<M>(object, method));
return true;
}
template <typename M>
bool disconnect(typename Slot<M>::type_value object, M method)
{
typename listValue_type::iterator ite = std::find_if(sloters.begin(), sloters.end(), FindHelper<M>(object, method));
if (ite != sloters.end()) {
delete (*ite);
sloters.erase(ite);
return true;
}
return false;
}
void eemit()
{
std::tuple<> para;
for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
{
(*ite)->dotask(para);
}
}
template <typename T0>
void eemit(T0 t)
{
std::tuple<T0> para = std::make_tuple(t);
for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
{
(*ite)->dotask(para);
}
}
template <typename T0, typename T1>
void eemit(T0 t, T1 t1)
{
std::tuple<T0, T1> para = std::make_tuple(t, t1);
for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
{
(*ite)->dotask(para);
}
}
template <typename T0, typename T1, typename T2>
void eemit(T0 t, T1 t1, T2 t2)
{
std::tuple<T0, T1, T2> para = std::make_tuple(t, t1, t2);
for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
{
(*ite)->dotask(para);
}
}
......
//使用Method来实例化list,是为了在connect的时候,防止Method和M不一致,也就是参数不一致。
typedef std::list<SlotBase<typename ParameterTupleTraits<ParameterTraits<Method> >::Parameters>* > listValue_type;
listValue_type sloters;
template <typename T>
struct FindHelper
{
FindHelper(typename Slot<T>::type_value object, T method):m_object(object),m_method(method)
{
}
bool operator()(const typename listValue_type::value_type& val) const
{
const Slot<T> *p = static_cast<Slot<T>*>(val);
return p->m_object == m_object && p->m_method == m_method;
}
bool operator()(typename listValue_type::value_type& val)
{
const Slot<T> *p = static_cast<Slot<T>*>(val);
return p->m_object == m_object && p->m_method == m_method;
}
typename Slot<T>::type_value m_object;
T m_method;
};
};
sloters完成了对各个sloter的保存。connect和disconnect实现了信号连接和断开,eemit实现了信号的发送,其也提供不同个数的参数的模板特化。FindHelper用来防止一个对象重复连接。
例子
#include <iostream>
#include "signal.h"
using namespace std;
struct TTT
{
int a;
int b;
int c;
};
class N
{
public:
virtual void func_c(int a, int b) = 0;
};
class A : public N
{
public:
A() {}
void func_b(int a) {cout << a << "aaaaaa\n";}
void func_c(int a, int b) {cout << a << "+" << b << "=" << a+b << std::endl;}
void func_a()
{
TTT t = {1, ‘s‘, ‘t‘};
s.eemit(t, t, t);
}
void func_z()
{
cout << "zhou xiang ";
}
Signal<void (*)(TTT, TTT, TTT)> s;
};
class B
{
public:
B(A *a):m_a(a){}
void func_b(int a) {cout << a << "bbbbbbb\n";}
void func_slot(TTT t, TTT t2, TTT t3)
{
cout << t.a + t2.b + t3.c << "-==-=-=-==-=\n";
}
void func_z()
{
cout << "love chenchen\n";
}
A *m_a;
void func_e()
{
m_a->s.connect(this, &B::func_slot);
}
};
int main()
{
A object;
B object1(&object);
Signal<void (*)(int, int)> s;
Signal<void (*)()> ssss;
ssss.connect(&object, &A::func_z);
ssss.connect(&object1, &B::func_z);
ssss.connect(&object1, &B::func_z);
// s.connect(&object ,&A::func_c);
// s.connect(&object ,&A::func_c);
// s.connect(&object ,&A::func_c);
// s.connect(&object ,&A::func_c);
// s.connect(&object ,&A::func_c);
// s.connect(&object ,&A::func_c);
// s.connect(&object ,&A::func_c);
// object1.func_e();
// object1.func_e();
// object.func_a();
ssss.eemit();
ssss.disconnect(&object, &A::func_z);
ssss.eemit();
cout << "---------------------------\n";
s.eemit(4, 456);
return 0;
}
这是我测试的例子。
这个只是一个雏形,多线程不能直接dotask,应该将调用投递到对象所在线程。
代码地址:https://git.oschina.net/zhouX/signal.git
时间: 2024-10-09 20:32:29