其是qt提供的对象间通信机制。ios
实现相似于信号与槽的机制,无非就是作一些薄记工做,qt中经过虚函数和moc来实现。那咱们怎么实现?个人思路是signal是一个对象,其余的slot将本身的回调注册进signal对象中,signal完成薄记工做。那么须要实现的细节归于一下几点。
1. 参数的提取与匹配。
2. slot容器的制做。
3. slot的调用。 c++
怎么处理参数是一大难题。咱们必须推导出各种型,包括类的类型和参数类型,在此咱们引入不少特性模板类。git
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++类成员函数的类类型和参数类型。markdown
//>>> 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。多线程
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)); }
其对不一样个数参数提供具体的模板特化。测试
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用来防止一个对象重复链接。
例子this
#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; }
这是我测试的例子。
spa
这个只是一个雏形,多线程不能直接dotask,应该将调用投递到对象所在线程。.net