c++可以用某种std::function表达任意的std::function吗?

codinghuang
  • 142
C++版本是11

我的需求是这样的:

我需要实现一个注册事件回调函数的方法,模型如下:

Server::on(std::string event_name, callback handler)
{
}

其中,callback的参数和event_name有关。例如:

int (*onReceive)(Server *, EventData *);
void (*onStart)(Server *serv);

也就是说,我需要用一种callback来表达出以下两种参数的回调函数:

(Server *, EventData *)
(Server *serv)

请问这个可以通过std::function实现吗?或者说有其他的方法实现?

举一个具体的例子,我知道代码会有问题,但是可以更加清晰的表达我的想法:

#include <iostream>
#include <cstdlib>
#include <functional>
#include <vector>

class server
{

public:

template <class... Args>
std::vector<std::string, std::function<void(Args... args)>> callbacks;

template <class... Args>
void on(std::string event_name, std::function<void(server *serv, Args... args)> fn)
{
    callbacks[event_name] = fn;
}

void event_loop(Event event)
{
    if (event == "start")
    {
        callbacks[event_name](this);
    }
    if (event == "receive")
    {
        callbacks[event_name](this, data);
    }
}
};

int main(int argc, char const *argv[])
{
    server serv;

    serv.on("Start", [](server *serv)
    {
    std::cout << "server start..." << std::endl;
    });

    serv.on("Receive", [](server *serv, EventData *data)
    {
    // 读取客户端发来的数据,处理业务逻辑
    });
    
    serv.start(); // 启动服务器

    return 0;
}
回复
阅读 1.9k
1 个回答
felix
  • 1.8k

std::function无法表达任意可调用对象。

如果你需要从callback调用,那么要让一组可调用对象构成重载。

  1. 为callback类型设计一个抽象类。参数用指针或智能指针传递。
  2. 在C++11下可通过模板元编程实现:这里根据你给出的接口设计,提供一个相对完整的实现:https://wandbox.org/permlink/...

如果不需要从callback调用,那么用类似std::variantboost::variant的容器作为形式参数,然后根据事件类型来赋值。


伪代码中的问题:

以下三点对你的伪代码都有不同程度的假设,没有详尽分析可能性。

  1. 调用server::on时,实际参数如果是lambda expression,就得提供模板参数(因为你这样写无法推导),即serv.on<EventData *>("Receive", [](server *serv, EventData *data) {});。如果你想要一个可以推导的写法,那么需要用到类似callable_trait的萃取,这个标准库是没有的。
  2. 函数event_loop里指明了调用时所有可能的参数表,这些参数表必须提供给callbacks。否则server本身得是一个模板才行。
  3. callbacks得是一个异质关联容器,你写的是变量模板声明。boost的c++11下模板元编程库mp11提供了这种容器。

如果以callbacks的声明为重点,那么你思路的技术难点是异质关联容器的访问和修改。你需要熟练掌握这种容器在C++11下的用法。标准库没有提供这方面的支持,最终也不是写一个server类能够完成的。
如果以调用callbacks[event_name](this);为重点,那么抽象类会是一个很好的解决方案,或者用variant
其余部分都会展开太多可能性。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏