C++ 可变参数回调

标签 c++ callback variadic-templates variadic

C++ 中的可变参数函数允许用户使用任意数量的参数调用函数。例如,sscanf 将要解析的字符串、格式字符串和一组将采用已解析项的值的参数作为输入。 如果我想让这个范例异步怎么办?我需要解析一些数据,从一些字节中提取可变数量的参数。需要提取的参数在 sscanf 中以格式字符串指定。我希望我的函数被这样调用:

function <void (int, int, int)> myfunc = [=] (int a, int b, int c)
{
    // Do something here!
};

asyncsscanf(my_bytes, "iii", myfunc);

asyncsscanf 应该进行所需的处理,完成后我希望它使用格式字符串中指定的正确参数调用 myfunc。 有可能做这样的事情吗?

谢谢

最佳答案

我不知道如何用你的方法做到这一点。

首先,asyncscanf 函数的第三个参数没有好的类型。在 asyncscanf 主体中唯一可接受的是 void(...)(函数接受无限数量的参数并且不返回任何内容),但是作为第三个传递给 asyncscanf 的参数应该是那种类型,这可能是 Not Acceptable 。

其次,您必须根据格式(示例中的“iii”)调度 my_bytes。如果您有有限数量的不同格式字符串(那么您可以“切换”所有可能的格式),则可以这样做。但在一般情况下,我认为这是不可能的。

但是因为您用“可变参数模板”标记了您的问题,所以我假设您使用的是 C++11/14。 也许您想将 asyncscanf 的格式参数设为更具可读性的模板参数(我假设格式在编译时始终已知)。下面是解决方案的片段。

#include <functional>
#include <iostream>

// template parsing function, remaining_bytes parameter should contain pointer to not parsed part
// of my_bytes
template <typename return_type> return_type parse(const char *my_bytes, const char *&remaining_bytes);

// specialization of parsing function for desired types, fake implementation
template <> int parse<int>(const char *my_bytes, const char *&remaining_bytes) {
    remaining_bytes = my_bytes;
    return 0;
}

// specialization of parsing function for desired types, fake implementation
template <> long parse<long>(const char *my_bytes, const char *&remaining_bytes) {
    remaining_bytes = my_bytes;
    return 1;
}

// declare helper template for general case
template <typename to_be_parsed_tuple, typename parsed_tuple>
struct asyncscanf_helper;

// all params parsed case
template <typename... parsed_params>
struct asyncscanf_helper<std::tuple<>, std::tuple<parsed_params...>> {
    void operator()(const char *, std::function<void(parsed_params...)> fun, parsed_params... params) {
        fun(params...);
    }
};

// some params to be parsed case
template <typename first_param_to_be_parsed, typename...to_be_parsed_params, typename... parsed_params>
struct asyncscanf_helper<std::tuple<first_param_to_be_parsed, to_be_parsed_params...>, std::tuple<parsed_params...>> {
    void operator()(const char *my_bytes, std::function<void(parsed_params..., first_param_to_be_parsed, to_be_parsed_params...)> fun, parsed_params... params) {
        const char *remaining_bytes = 0;
        first_param_to_be_parsed p1 = parse<first_param_to_be_parsed>(my_bytes, remaining_bytes);
        asyncscanf_helper<std::tuple<to_be_parsed_params...>, std::tuple<parsed_params..., first_param_to_be_parsed>>()(remaining_bytes, fun, params..., p1);
    }
};

template <typename... params>
void asyncscanf(const char *my_bytes, void function(params...)) {
    asyncscanf_helper<std::tuple<params...>, std::tuple<>>()(my_bytes, function);
}

void test_fun(int a, int b, int c) {
    std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
}

void test_fun2(int a, long b, int c) {
    std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
}

int main() {
    asyncscanf("1 2 3", test_fun);
    asyncscanf("1 2 3", test_fun2);
}

代码注释:

  • 的想法是我们有两个参数包:一个用于尚未解析的参数,一个用于已经解析的参数,并将参数从第一个包一个一个地传递到第二个;解析完所有参数后,只需调用函数
  • 如果您忘记为作为第三个参数传递给 asyncscanf 的函数所需的类型专门化解析函数,编译器会告诉您。
  • 我不得不使用模板结构 asyncscanf_helper 和元组而不是简单的函数模板 asyncscanf_helper,因为在一个模板函数中使用两个参数包会出现问题。
  • 我在 asyncscanf_helper 中使用了 std::function,因为它更通用,您可以使用例如lambdas 作为您的参数,但目前我将 asyncscanf 中的标准函数保留为参数类型,否则它的第二个参数必须显式转换为具有适当签名的 std::function 或必须显式声明模板参数。
  • 由于解析函数特化的虚假实现,如果您运行代码,您将看不到预期的结果,但因为解析不是您问题的一部分,所以我将其保留为模拟。

关于C++ 可变参数回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27758863/

相关文章:

jQuery:在自定义 jQuery 函数的回调函数中使用 $(this)

c++ - 成员函数的正确函数签名模板

c++ - 静态变量阴影全局

Android WebView JavaScript 回调到原始 View

javascript - 带参数调用回调

c++ - 在模板函数 C++ 中的 F&&,Args &&... 参数之后添加另一个函数作为参数

c++ - 从参数包中提取成员类型

c++ - QT Start explorer进程参数

c++ - 如何在 C++ - Linux 中获取当前源路径

c++ - 插入()或制作新的