zoukankan      html  css  js  c++  java
  • c++:muduo使用template保存std::function的参数类型

    概览

    最近看到陈硕老师的muduo的7.6实现protobuf编解码器与消息分发器,觉得消息分发器这里写的确实很妙,简述一下背景,做业务的时候我们常会在tcp上制定一个消息格式,通过这些消息进行通讯,消息除了长度,类型,消息体为了最大压缩会使用pb,然后陈老师制作一个如下的消息格式样例

    +-+-+-+-+-+-+-+-+-+-+-
    +-      len         +- 4 bytes : 包的总长度
    +- pb type name len +- :pb 类型名字长度
    +-    pb type     +- : pb 类型名字,提供反序列化功能
    +-    pb data       +- : pb的序列化数据
    +-    check sum     +- 4 bytes: 校验码
    +-+-+-+-+-+-+-+-+-+-+-
    

    (虽然我个人觉得第二项pb类型名字没用,因为pb type以为结束已经很好的起到了分割符的作用了),当我们收到这个包的时候,进行解包通过pb type和pb data就可以得到一个完整的pb,但是接收pb的回调函数是muduo中处理的很精妙的一点,

    因为函数指针以及std::function是不支持多态的,对于一个底层的消息分发器而言要保存所有消息的回调,是无法使用一个函数指针数组去保存的,muduo使用了一个类CallBack将回调函数放在了里面,因为所有的pb消息都继承于message,在这个function使用dynamic_cast将message做了一次转化转成了具体的子类消息,然后才将消息传递到具体的callback中,如下图, 这里很巧妙的一点是使用模板,将子类消息类型保存下来以便于dynamic_cast做转换,同时所有的CallBack都继承一个基类CallBack,那么只需要保存CallBackT指针在分发器中,利用虚函数多态进行调用即可。

    2.正文

    如概览所描述,muduo使用了一个类CallBack将回调函数放在了里面,使用模板,将子类消息类型保存下来以便于dynamic_cast做转换,同时所有的CallBack都继承一个基类CallBack,那么只需要保存CallBackT指针在分发器中,利用虚函数多态进行调用;来看看一个简单的例子实现

    #include <iostream>
    #include <algorithm>
    #include <optional>
    #include <vector>
    #include <map>
    #include <string>
    #include <sstream>
    #include <chrono>
    #include <thread>
    #include <functional>
    #include <thread>
    #include <condition_variable>
    #include <atomic>
    #include <memory>
    #include <mutex>
    #include <any>
    using namespace std;
    
    std::mutex mtx;
    std::condition_variable cv;
    
    class MsgBase
    {
    public:
        virtual ~MsgBase() {}
    };
    
    class MsgDerive1 : public MsgBase
    {
    public:
        ~MsgDerive1() override {}
    
        static std::string GetMsgType()
        {
            return "msg_derive1";
        }
    };
    
    class CallBack
    {
    public:
        virtual void OnCallBack(const MsgBase &msg) = 0;
    };
    
    template <typename T>
    class CallBackT : public CallBack
    {
    public:
        typedef std::function<void(const T &msg_derive)> CallBackFun;
        CallBackFun cb_;
    
        CallBackT(const CallBackFun &cb) : cb_(cb) {}
    
        // 1
        void OnCallBack(const MsgBase &msg) override
        {
            const T &msg_specify = dynamic_cast<const T &>(msg); // 转换
            cb_(msg_specify); // 调用真正的回调函数
        }
    };
    
    void OutPutDeriveMsg(const MsgDerive1 &msg_derive)
    {
        std::cout << msg_derive.GetMsgType() << std::endl;
    }
    
    int main()
    {
    
        std::map<std::string, std::shared_ptr<CallBack>> call_back_map;
    
        // 注册监听MsgDerive1 消息的函数
        std::shared_ptr<CallBackT<MsgDerive1>> call_back_t(new CallBackT<MsgDerive1>(std::bind(OutPutDeriveMsg, std::placeholders::_1)));
        call_back_map.insert(std::pair(MsgDerive1::GetMsgType(), call_back_t));
    
        //a MsgDerive1 come
        MsgDerive1 msg_derive1;
        std::shared_ptr<CallBack> msg_handle_call_back = call_back_map.find(msg_derive1.GetMsgType())->second;
        msg_handle_call_back->OnCallBack(msg_derive1);
        return 0;
    }
    
    

    main()中创建了一个call_back_map,就把它当作消息分发器来保存回调对象,可以看到value是一个std::shared_ptr<CallBack>, 然后接下来创建一个MsgDerive1的CallBack对象并将其放入这个map中,接下来一个MsgDerive1消息过来了,通过在这个map中找到这个消息类型的callback对象,然后调用虚函数OnCallBack(),也就是CallBackT::OnCallBack(), 在这里面我们看到对消息进行dynamic_cast处理完成后,调用我们一开始放入的std::function,很妙~

    3/ref

    • 3.1 <linux多线程服务器编程>
  • 相关阅读:
    消息路由的构成
    消息的构造
    消息传递通道
    消息传递系统
    EXtJS Ext.data.Model
    oracle coherence介绍及使用
    LINUX下安装和配置WEBLOGIC10.0.3
    WebLogic中"域"的概念
    WebLogic中的一些基本概念
    下属有能力却不服管,你该怎么办?
  • 原文地址:https://www.cnblogs.com/ishen/p/14232467.html
Copyright © 2011-2022 走看看