(原創)一個輕量級、高性能的消息分發器的實現


一個消息分發器應該要具備以下幾個特征:

  • 集中注冊消息以及消息處理函數;
  • 可以處理任何類型的消息;
  • 根據消息選擇正確的消息處理函數;
  • 能檢測消息的正確性;

  要實現一個消息分發器的難點在如何能處理所有的消息,因為不同的消息的消息處理函數是不同的,有可能不同的消息處理函數的返回值、形參都不同,目前還沒有一種容器能將所有的函數,諸如 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)...);
    }
}

測試代碼:

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;
}

輸出結果:

call1
1

  這個分發器是在編譯期生成的,分發也是在編譯期完成的,所以分發效率很高。當消息和消息處理函數不匹配時,就會出現編譯錯誤,保證在編譯期就能檢查消息是否是正確的。
  上例中實現了將不同的消息分發給不同的消息處理函數,而這些消息處理函數是各不相同的,不管這些消息處理函數的返回值是否相同,參數是否相同,都能實現分發。實現代碼不過幾十行,可以說是非常輕量級的分發器了。

 

如果你覺得這篇文章對你有用,可以點一下推薦,謝謝。

c++11 boost技術交流群:296561497,歡迎大家來交流技術。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM