如何将 std::function 对象传递给采用函数指针的函数?

Posted

技术标签:

【中文标题】如何将 std::function 对象传递给采用函数指针的函数?【英文标题】:How do I pass a std::function object to a function taking a function pointer? 【发布时间】:2016-09-18 20:35:25 【问题描述】:

我正在尝试与使用这种熟悉模式的c 编写的库进行交互:

void some_c_handler(void(*func)(void*), void* data);

现在,我想为这个函数编写一个 C++ 包装器,如下所示:

void my_new_cpp_handler(std::function<void()>&& func)

  void (*p)() = foo(func);
  void* data = bar(func);
  some_c_handler(p, data);

some_c_handlermy_new_cpp_handler 都在解决同一个问题;他们正在接受某种功能以及某种状态。但后者更受欢迎,因为它从用户那里抽象出许多实现细节,并允许简单地传入一个 lambda 对象。

所以my_new_cpp_handler 应该接受给定的func 参数,将其转换为函数指针并将其状态传递给data

我对标准或std::function 的实施知之甚少,不知道这是否是一个合理的要求。 foobar可以存在吗?

换句话说,我想要的是能够将有状态函数传递给c 回调处理程序,而不必手动实例化我自己的struct 类型来传递它。显然std::function 已经为我们完成了这项工作,所以我希望能够以某种方式将函数指针与状态分开并将其传递给c 样式的处理程序。这可能吗?

【问题讨论】:

【参考方案1】:

这可能吗?

没有。

您可以将 C 风格的回调包装到 std::function&lt;&gt;... 中,编译器将发出代码以提取 datahandler 并调用它。但是,执行此操作的确切代码将取决于编译器、ABI 和所使用的标准 C++ 库。仅给定 std::function 包装器,您无法神奇地重构该代码。

此外,任意 std::function...(或 lambda)可以包装代码其他,而不是对 C 样式处理程序的调用。很明显,应用“神奇地重构”代码从std::function... 这样的实例中提取 C 风格的处理程序是不可能成功的。

附:

换句话说,我想要的是能够将有状态函数传递给 c 回调处理程序,而不必手动实例化我自己的结构类型来传递它。显然std::function 已经为我们完成了这项工作

那么为什么你只使用std::function已经为你做的事情:

void my_new_cpp_handler(std::function<void()>&& func)

  func();  // calls some_c_handler(p, data) IFF that is what "func" encodes.

【讨论】:

【参考方案2】:

您可以创建一个包装函数,其目的是简单地执行std::function 回调。

void some_c_handler(void(*)(void*), void*) 

void std_function_caller(void* fn)  
    (*static_cast<std::function<void()>*>(fn))();
;

auto make_std_function_caller(std::function<void()>& fn)  
    return std::make_pair(std_function_caller, static_cast<void*>(&fn));


void my_new_cpp_handler(std::function<void()>&& func)  
    const auto p = make_std_function_caller(func);
    some_c_handler(p.first, p.second);

【讨论】:

【参考方案3】:

如果您仔细查看此库的文档,您会发现

void some_c_handler(void(*func)(void*), void* data);

调用func,将data 参数传递给它。

对于采用回调函数的 C 库来说,这是一种非常常见的设计模式。除了回调函数之外,它们还采用了一个额外的不透明指针,该指针不被库解释,而是盲目地转发到func。换句话说,C 库调用

 func(data);

您可以在 C++ 代码中使用它来将普通指针传递给任何类。

这也包括std::function

诀窍是在大多数情况下都需要使用new

auto *pointer=new std::function< function_type >...

最终结果是一个可以传递给 C 库的指针,以及一个指向“trampoline 函数”的指针:

some_c_handler(&cpp_trampoline, reinterpret_cast<void *>(pointer));

而蹦床重铸不透明指针:

void cpp_trampoline(void *pointer)

   auto real_pointer=reinterpret_cast<std::function< ... >*>(pointer);

   // At this point, you have a pointer to the std::function here.
   // Do with it as you wish.

您需要在这里解决的唯一细节是找出动态分配的函数指针的正确范围,以避免内存泄漏。

【讨论】:

【参考方案4】:

根据this 链接,std::function 对象没有可以提供对指针的原始访问的可访问成员。您可能应该定义一个struct,其中包含指向函数指针和对象的指针,以及在构造std::struct之前将指针的地址存储到结构的构造函数包装器,以便分配存储在它指向您的 C 处理程序参数的指针。

【讨论】:

以上是关于如何将 std::function 对象传递给采用函数指针的函数?的主要内容,如果未能解决你的问题,请参考以下文章

将 std::function 作为参数传递给 for_each

如何将此 std::function 传递给 std::async

JavaScript:如何将多个参数传递给对象成员内的回调函数?

如何将日期对象作为第一个也是唯一的输入参数传递给 PL-SQL

如何将库类或对象传递给片段?

如何将 json 对象传递给 mvc 控制器