• (原创)一个轻量级、高性能的消息分发器的实现


    一个消息分发器应该要具备以下几个特征:

    • 集中注册消息以及消息处理函数;
    • 可以处理任何类型的消息;
    • 根据消息选择正确的消息处理函数;
    • 能检测消息的正确性;

      要实现一个消息分发器的难点在如何能处理所有的消息,因为不同的消息的消息处理函数是不同的,有可能不同的消息处理函数的返回值、形参都不同,目前还没有一种容器能将所有的函数,诸如 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,欢迎大家来交流技术。

  • 相关阅读:
    [HAOI2008]糖果传递
    LGTB 与大数
    LGTB 与序列
    poj1160 Post Office
    组队
    [JLOI2015]装备购买
    三元组
    乘法表
    [BZOJ3730]震波
    [Luogu3345][ZJOI2015]幻想乡战略游戏
  • 原文地址:https://www.cnblogs.com/qicosmos/p/3586129.html
Copyright © 2020-2023  润新知