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

qua*_*ant 8 c c++

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

void some_c_handler(void(*func)(void*), void* data);
Run Code Online (Sandbox Code Playgroud)

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

void my_new_cpp_handler(std::function<void()>&& func)
{
  void (*p)() = foo(func);
  void* data = bar(func);
  some_c_handler(p, data);
}
Run Code Online (Sandbox Code Playgroud)

双方some_c_handler并my_new_cpp_handler正在解决相同的问题; 他们与某个州一起承担某种功能.但后者是首选的,因为它从用户抽象出大部分实现细节,并允许简单地传入lambda对象.

因此,my_new_cpp_handler应该获取func它给出的参数,将其转换为函数指针并将其状态传递给data.

我对标准或执行情况了解不够,std::function知道这是否是一个合理的要求.能foo和bar存在吗?

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

Emp*_*ian 8

这可能吗?

没有.

你可以用C风格的回调成std::function<>...,编译器会发出代码提取data和handler并调用它.但是,这样做的确切代码将取决于编译器,ABI以及正在使用的标准C++库.只有std::function包装器才能神奇地重建代码.

此外,任意的std::function...(或λ)可以包裹代码其他比C风格处理程序的调用.很明显,应用"神奇重建"的代码从这样的实例中提取C风格的处理程序std::function...是不可能成功的.

PS

换句话说,我想要的是能够将有状态函数传递给ac回调处理程序,而不必手动实例化我自己的结构类型来传递它.显然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.
}
Run Code Online (Sandbox Code Playgroud)


Sam*_*hik 6

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

void some_c_handler(void(*func)(void*), void* data);
Run Code Online (Sandbox Code Playgroud)

调用func,传递data参数.

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

 func(data);
Run Code Online (Sandbox Code Playgroud)

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

这也包括在内std::function.

诀窍在于,在大多数情况下,有必要使用new:

auto *pointer=new std::function< function_type >...
Run Code Online (Sandbox Code Playgroud)

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

some_c_handler(&cpp_trampoline, reinterpret_cast<void *>(pointer));
Run Code Online (Sandbox Code Playgroud)

蹦床重铸了不透明的指针:

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.
Run Code Online (Sandbox Code Playgroud)

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


Bri*_*ian 5

您可以创建一个包装函数,其目的是简单地执行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);
}
Run Code Online (Sandbox Code Playgroud)