我有一个基于 ZeroMQ 和 Protobuf 的 C++ 服务器。 我正在寻找一种反序列化和发送消息的惯用方式。在 Python 中,我将使用字典将函数与 protobuf 消息的每个名称相关联。
我目前拥有的是:
if (*req_msg_name == "SayHelloReq")
{
SPDLOG_LOGGER_DEBUG(logger, "Received SayHelloReqrequest");
axi_ethernet::SayHelloReq msg_req;
axi_ethernet::SayHelloRep msg_rep;
msg_req.ParseFromString(*req_msg_content);
say_hello_function(&msg_req, &msg_rep);
msg_rep.SerializeToString(rep_msg_content);
*rep_msg_name = msg_rep.GetDescriptor()->name();
msg_rep.SerializeToString(rep_msg_content);
}
else if (*req_msg_name == "ReadFileReq")
{
SPDLOG_LOGGER_DEBUG(logger, "Received ReadFileReq request");
axi_ethernet::ReadFileReq msg_req;
axi_ethernet::ReadFileRep msg_rep;
msg_req.ParseFromString(*req_msg_content);
read_file_function(&msg_req, &msg_rep);
msg_rep.SerializeToString(rep_msg_content);
*rep_msg_name = msg_rep.GetDescriptor()->name();
msg_rep.SerializeToString(rep_msg_content);
}
这个
if/else
语句相当长,并且这段代码中的很多行都是重复的。
每个块中执行的操作是:
我正在努力最小化添加新消息的开销。
我正在考虑创建一个像
std::map<string, std::tuple<msg_req_type, msg_rep_type, ptr_processing_function>
这样的地图,其中字符串是*req_msg_name
,并运行需要调用的处理函数。但是我应该如何定义我的函数定义,因为每个函数的参数类型都不同(但都继承自 protobuf::Message
)。
如果我设法使用地图,我可能会摆脱
if/else
语句。
添加对新消息的支持只需向地图添加新项目即可。
我的问题是:如何定义元组和函数指针,以便它可以接受不同类型的 protobuf 消息。
我试过了,但不起作用
using pfunc = void(*)(google::protobuf::Message* req_msg, google::protobuf::Message* rep_msg);
std::map<std::string, pfunc> map;
我将使用一个类接口,模板处理以 protobuf 消息作为参数的函数的常见情况:
class MessageHandler
{
public:
virtual void handle_message(std::string protobuf_data) = 0;
};
template <typename ReqType, typename RepType>
class GenericMessageHandler: public MessageHandler
{
public:
GenericMessageHandler(std::function<RepType, ReqType> function):
m_function(function) {}
virtual handle_message(std::string protobuf_data)
{
SPDLOG_LOGGER_DEBUG(logger, "Received SayHelloReqrequest");
ReqType msg_req;
RepType msg_rep;
msg_req.ParseFromString(*req_msg_content);
m_function(&msg_req, &msg_rep);
msg_rep.SerializeToString(rep_msg_content);
*rep_msg_name = msg_rep.GetDescriptor()->name();
msg_rep.SerializeToString(rep_msg_content);
}
private:
std::function<void, ReqType, RepType> m_function;
};
主程序将包含从消息名称到处理程序类的映射:
std::map<std::string, MessageHandler> handlers;
handlers["SayHelloReq"] = GenericMessageHandler<axi_ethernet::SayHelloReq,
axi_ethernet::SayHelloRep>(say_hello_function);
handlers["ReadFileReq"] = GenericMessageHandler<axi_ethernet::ReadFileReq,
axi_ethernet::ReadFileRep>(read_file_function);
handlers.at(req_msg_name).handle_message(req_msg_content);