首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何将C++λ传递给需要函数指针和上下文的C回调?

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

Stack Overflow用户
提问于 2013-12-12 01:30:22
回答 4查看 16.7K关注 0票数 37

我正在尝试在使用标准函数指针+上下文范例的C-API中注册一个回调。下面是api的外观:

代码语言:javascript
复制
void register_callback(void(*callback)(void *), void * context);

我真正想做的是能够注册一个C++ lambda作为回调函数。此外,我希望lambda是一个有捕获变量(即。无法转换为直接的无状态std::function)

我需要编写什么样的适配器代码才能将lambda注册为回调?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-12-12 01:56:08

简单的方法是将lambda放入保存在某个地方的std::function<void()>中。它可能是在堆上分配的,并且仅由向接受回调的实体注册的void*引用。然后,回调就会是如下所示的函数:

代码语言:javascript
复制
extern "C" void invoke_function(void* ptr) {
    (*static_cast<std::function<void()>*>(ptr))();
}

请注意,std::function<S>可以保存具有状态的函数对象,例如具有非空捕获的lambda函数。您可以像这样注册一个回调:

代码语言:javascript
复制
register_callback(&invoke_function,
  new std::function<void()>([=](){ ... }));
票数 23
EN

Stack Overflow用户

发布于 2013-12-12 02:52:54

最有效的方法是直接对lambda进行voidify

代码语言:javascript
复制
#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();
}

在这里,voidify接受一个lambda参数(可选)和一个参数列表,并生成一个传统的C风格的回调-void*对。void*归一个具有特殊删除程序的unique_ptr所有,因此它的资源得到了适当的清理。

std::function解决方案相比,这种方法的优势在于效率--我消除了一级运行时间接性。回调有效的生存期也很清楚,因为它在voidify返回的std::unique_ptr<void, void(*)(void*)>中。

如果您想要更复杂的生命周期,可以将unique_ptr<T,D>s moved到shared_ptr<T>中。

上面的代码混合了生存期和数据,类型擦除和实用程序。我们可以分成两部分:

代码语言:javascript
复制
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)
  };
}

现在voidify不分配。只需在回调的整个生命周期中存储您的voidify,将一个指向second的指针作为您的void*first函数指针一起传递。

如果您需要将此结构存储在堆栈之外,将lambda转换为std::function可能会有所帮助。或者使用上面的第一个变体。

代码语言:javascript
复制
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";
}
票数 17
EN

Stack Overflow用户

发布于 2018-01-18 16:20:22

只要lambda函数没有捕获变量,它就与C-callback函数兼容。

强迫用新的方式把新的东西放到旧的东西上是没有意义的。

用老式的方式怎么样?

代码语言:javascript
复制
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);
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20525977

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档