其是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