C++:调度 protobuff 消息的惯用方式

问题描述 投票:0回答:1

我有一个基于 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
语句相当长,并且这段代码中的很多行都是重复的。 每个块中执行的操作是:

  • 创建对应的protobuf消息
  • 反序列化传入消息
  • 调用一个以请求和回复消息作为参数的函数
  • 将 Reply 消息序列化为字符串
  • 将消息名称获取为字符串(以添加到 ZMQ 消息中)。

我正在努力最小化添加新消息的开销。

我正在考虑创建一个像

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;
c++ protocol-buffers
1个回答
0
投票

我将使用一个类接口,模板处理以 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);
© www.soinside.com 2019 - 2024. All rights reserved.