标签:std function muduo +- pb CallBack msg include
概览
最近看到陈硕老师的muduo的7.6实现protobuf编解码器与消息分发器,觉得消息分发器这里写的确实很妙,简述一下背景,做业务的时候我们常会在tcp上制定一个消息格式,通过这些消息进行通讯,消息除了长度,类型,消息体为了最大压缩会使用pb,然后陈老师制作一个如下的消息格式样例
+-+-+-+-+-+-+-+-+-+-+-
+- len +- 4 bytes : 包的总长度
+- pb type name len +- :pb 类型名字长度
+- pb type \0+- : pb 类型名字,提供反序列化功能
+- pb data +- : pb的序列化数据
+- check sum +- 4 bytes: 校验码
+-+-+-+-+-+-+-+-+-+-+-
(虽然我个人觉得第二项pb类型名字没用,因为pb type以为\0结束已经很好的起到了分割符的作用了),当我们收到这个包的时候,进行解包通过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多线程服务器编程>
标签:std,function,muduo,+-,pb,CallBack,msg,include 来源: https://www.cnblogs.com/ishen/p/14232467.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。