一個消息分發器應該要具有如下幾個特徵:c++
要實現一個消息分發器的難點在如何能處理全部的消息,由於不一樣的消息的消息處理函數是不一樣的,有可能不一樣的消息處理函數的返回值、形參都不一樣,目前尚未一種容器能將全部的函數,諸如 void f(); void f1(int); int f2(double); double f3(int, double)等函數放到一個容器中。若是真的存在這種容器的話,那就能夠將消息和消息函數做爲一個pair存放到這個容器中,分發消息的時候就能夠根據消息選擇對應的消息處理函數了。因此關鍵是要實現一個能容納全部函數的東西。函數
能實現將不一樣類型存儲起來目前有兩種方法:第一種經過any,不過用any的話,取值時any_cast<T>須要具體的類型,即須要具體的消息處理函數,這不能實現消息的自動分發,所以不知足要求;第二種是經過tuple,由於tuple能夠容納任意個數和任意類型的元素。若是將消息放到一個tuple中,再將消息對應的處理函數放到另一個tuple中,而後將這兩個tuple合併成爲一個新的tuple,這個新tuple中的元素是一個鍵值對,鍵爲消息,值爲處理函數,而後外面就能夠根據這個消息實現消息的分發處理了。看看具體的實現吧:測試
#include <tuple>
namespace Cosmos { namespace details { //tuple參數的索引序列 template<int...> struct IndexTuple{}; template<int N, int... Indexes> struct MakeIndexes : MakeIndexes<N - 1, N - 1, Indexes...>{}; template<int... indexes> struct MakeIndexes<0, indexes...> { typedef IndexTuple<indexes...> type; }; template<std::size_t N, typename T1, typename T2> using pair_type = std::pair<typename std::tuple_element<N, T1>::type, typename std::tuple_element<N, T2>::type>; template<std::size_t N, typename T1, typename T2> pair_type<N, T1, T2> pair(const T1& tup1, const T2& tup2) { return std::make_pair(std::get<N>(tup1), std::get<N>(tup2)); } template<int... Indexes, typename T1, typename T2> auto pairs_helper(IndexTuple<Indexes...>, const T1& tup1, const T2& tup2) -> decltype(std::make_tuple(pair<Indexes>(tup1, tup2)...)) { return std::make_tuple(pair<Indexes>(tup1, tup2)...); } } // namespace details template<typename Tuple1, typename Tuple2> auto Zip(Tuple1 tup1, Tuple2 tup2) -> decltype(details::pairs_helper(typename details::MakeIndexes<std::tuple_size<Tuple1>::value>::type(), tup1, tup2)) { static_assert(std::tuple_size<Tuple1>::value == std::tuple_size<Tuple2>::value, "tuples should be the same size."); return details::pairs_helper(typename details::MakeIndexes<std::tuple_size<Tuple1>::value>::type(), tup1, tup2); } template<typename F, typename... Args> auto Apply(F&&f, Args&&... args)->decltype(f(args...)) { return std::forward<F>(f)(std::forward<Args>(args)...); } }
測試代碼:spa
void Call1() { cout << "call1" << endl; } int Call2(int a) { return a; } enum EnumMessage { Message1, Message2 }; template<int key, typename R, typename... Args> R Dispatch(Args... args) { auto tpkey = std::make_tuple(Message1, Message2); auto tpval = std::make_tuple(Call1, Call2); auto pairs = Cosmos::Zip(tpkey, tpval); return Cosmos::Apply(std::get<key>(pairs).second, args...); } int main() { Dispatch<Message1, void>(); auto r = Dispatch<Message2, int>(1); cout << r << endl; return 0; }
輸出結果:c++11
call1 1
這個分發器是在編譯期生成的,分發也是在編譯期完成的,因此分發效率很高。當消息和消息處理函數不匹配時,就會出現編譯錯誤,保證在編譯期就能檢查消息是不是正確的。
上例中實現了將不一樣的消息分發給不一樣的消息處理函數,而這些消息處理函數是各不相同的,無論這些消息處理函數的返回值是否相同,參數是否相同,都能實現分發。實現代碼不過幾十行,能夠說是很是輕量級的分發器了。code
若是你以爲這篇文章對你有用,能夠點一下推薦,謝謝。blog
c++11 boost技術交流羣:296561497,歡迎你們來交流技術。索引