简体   繁体   English

如何将 C++ lambda 传递给需要函数指针和上下文的 C 回调?

[英]How can I pass a C++ lambda to a C-callback that expects a function pointer and a context?

I'm trying to register a callback in a C-API that uses the standard function-pointer+context paradigm.我正在尝试在使用标准函数指针+上下文范例的 C-API 中注册回调。 Here's what the api looks like:这是 api 的样子:

void register_callback(void(*callback)(void *), void * context);

What I'd really like to do is be able to register a C++ lambda as the callback.我真正想做的是能够将 C++ lambda 注册为回调。 Additionally, I want the lambda to be one that has captured variables (ie. can't be converted to a straight stateless std::function )此外,我希望 lambda 是一个已捕获变量的(即不能转换为直接无状态std::function

What kind of adapter code would I need to write to be able to register a lambda as the callback?我需要编写什么样的适配器代码才能将 lambda 注册为回调?

The simple approach is to stick the lambda into a std::function<void()> which is kept somewhere.简单的方法是将 lambda 粘贴到保存在某处的std::function<void()>中。 Potentially it is allocated on the heap and merely referenced by the void* registered with the entity taking the callback.可能它是在堆上分配的,并且仅被注册到接受回调的实体的void*引用。 The callback would then simply be a function like this:回调将只是一个像这样的函数:

extern "C" void invoke_function(void* ptr) {
    (*static_cast<std::function<void()>*>(ptr))();
}

Note that std::function<S> can hold function objects with state, eg, lambda functions with a non-empty capture.请注意, std::function<S>可以保存具有状态的函数对象,例如,具有非空捕获的 lambda 函数。 You could register a callback like this:您可以像这样注册回调:

register_callback(&invoke_function,
  new std::function<void()>([=](){ ... }));

The most efficient way is to voidify the lambda directly.最有效的方法是直接使 lambda voidify

#include <iostream>
#include <tuple>
#include <memory>

template<class...Args>
struct callback {
  void(*function)(void*, Args...)=nullptr;
  std::unique_ptr<void, void(*)(void*)> state;
};
template<typename... Args, typename Lambda>
callback<Args...> voidify( Lambda&& l ) {
  using Func = typename std::decay<Lambda>::type;
  std::unique_ptr<void, void(*)(void*)> data(
    new Func(std::forward<Lambda>(l)),
    +[](void* ptr){ delete (Func*)ptr; }
  );
  return {
    +[](void* v, Args... args)->void {
      Func* f = static_cast< Func* >(v);
      (*f)(std::forward<Args>(args)...);
    },
    std::move(data)
  };
}

void register_callback( void(*function)(void*), void * p ) {
  function(p); // to test
}
void test() {
  int x = 0;
  auto closure = [&]()->void { ++x; };
  auto voidified = voidify(closure);
  register_callback( voidified.function, voidified.state.get() );
  register_callback( voidified.function, voidified.state.get() );
  std::cout << x << "\n";
}
int main() {
  test();
}

here voidify takes a lambda and (optionally) a list of arguments, and generates a traditional C-style callback- void* pair.这里voidify接受一个 lambda 表达式和(可选)一个参数列表,并生成一个传统的 C 风格回调void*对。 The void* is owned by a unique_ptr with a special deleter so its resources are properly cleaned up. void*由具有特殊删除器的unique_ptr拥有,因此其资源被正确清理。

The advantage of this over a std::function solution is efficiency -- I eliminated one level of run-time indirection.std::function解决方案相比,它的优势在于效率——我消除了一层运行时间接。 The lifetime that the callback is valid is also clear, in that it is in the std::unique_ptr<void, void(*)(void*)> returned by voidify .回调有效的生命周期也很清楚,因为它在voidify返回的std::unique_ptr<void, void(*)(void*)>

unique_ptr<T,D> s can be move d into shared_ptr<T> if you want a more complex lifetime.如果您想要更复杂的生命周期unique_ptr<T,D>可以将unique_ptr<T,D> s moveshared_ptr<T>中。


The above mixes lifetime with data, and type erasure with utility.上面将生命周期与数据混合在一起,将类型擦除与实用程序混合在一起。 We can split it:我们可以拆分它:

template<class Lambda, class...Args>
struct callback {
  void(*function)(void*, Args...)=nullptr;
  Lambda state;
};

template<typename... Args, typename Lambda>
callback<typename std::decay<Lambda>::type, Args...> voidify( Lambda&& l ) {
  using Func = typename std::decay<Lambda>::type;
  return {
    +[](void* v, Args... args)->void {
      Func* f = static_cast< Func* >(v);
      (*f)(std::forward<Args>(args)...);
    },
    std::forward<Lambda>(l)
  };
}

Now voidify does not allocate.现在voidify不分配。 Simply store your voidify for the lifetime of the callback, passing a pointer-to- second as your void* along side the first function pointer.只需在回调的生命周期内存储您的 voidify,将指向second的指针作为您的void*first函数指针一起传递。

If you need to store this construct off the stack, converting the lambda to a std::function may help.如果您需要将此构造存储在堆栈外,将 lambda 转换为std::function可能会有所帮助。 Or use the first variant above.或者使用上面的第一个变体。

void test() {
  int x = 0;
  auto closure = [&]()->void { ++x; };
  auto voidified = voidify(closure);
  register_callback( voidified.function, &voidified.state );
  register_callback( voidified.function, &voidified.state );
  std::cout << x << "\n";
}

A lambda function is compatible with C-callback function as long as it doesn't have capture variables.只要 lambda 函数没有捕获变量,它就与 C 回调函数兼容。
Force to put something new to old one with new way doesn't make sense.强行用新的方式把新的东西放到旧的东西上是没有意义的。
How about following old-fashioned way?如何遵循老式的方式?

typedef struct
{
  int cap_num;
} Context_t;

int cap_num = 7;

Context_t* param = new Context_t;
param->cap_num = cap_num;   // pass capture variable
register_callback([](void* context) -> void {
    Context_t* param = (Context_t*)context;
    std::cout << "cap_num=" << param->cap_num << std::endl;
}, param);

A very simple way to get use a lamda function as a C funtion pointer is this:将 lamda 函数用作 C 函数指针的一种非常简单的方法是:

auto fun=+[](){ //add code here }

see this answer for a example.请参阅此答案以获取示例。 https://stackoverflow.com/a/56145419/513481 https://stackoverflow.com/a/56145419/513481

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C / C ++编译器是否可以内联C-Callback函数? - Is there any way a C/C++ compiler can inline a C-Callback Function? c ++如何通过引用将迭代器指针传递给期望对象的函数 - c++ How to pass iterator pointer to function that expects the object by reference 如何在C ++中传递通用函数指针? - How can I pass generic function pointer in c++? 对 function 模板的 C 回调:显式实例化模板 - C-callback to function template: explicitly instantiate template 我如何用Functor调用C函数,该函数期望指向C ++中的void函数的指针? - How do I call a C-function with a Functor that expects a pointer to a void function in C++? 如何使用C ++ lambda将成员函数指针转换为普通函数指针以用作回调 - How to use a C++ lambda to convert a member function pointer to a normal function pointer for use as a callback 如何在C ++中传递函数指针并将其与函数进行比较? - How can you pass a function pointer and compare it in a function in C++? 如何将C ++回调传递给C库函数? - How do I pass a C++ callback to a C library function? C ++回调函数指针 - C++ Callback function pointer C++ Lambda 到函数指针 - C++ Lambda to Function Pointer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM