将lambdas传递给传统的回调

时间:2015-02-10 10:28:03

标签: c++ c++11 lambda

我在C ++ 11项目中使用C库,这个C库提供了一个需要函数指针的函数。除非我捕获变量,否则我想将C ++ 11 lambda传递给它,它正常工作。这是一个简短的例子:

#include <cstdio>
#include <functional>

typedef int (*Callback)();

void legacy(Callback callback) {
    printf("%i\n", callback());
}

int stdCallback() {    
    return 1; 
}

int main(int argc, char* argv[]) {
    int number = 3;    

    // Standard C callback works
    legacy(stdCallback);

    // Lambda without capturing works
    legacy([]() { return 2; });    

    // Lambda with capturing doesn't work
    legacy([&]() { return number; });

    return 0;
}

GNU C ++编译器在第三次调用legacy函数时给出了以下错误消息:

test.cpp: In function ‘int main(int, char**)’:
test.cpp:24:36: error: cannot convert ‘main(int, char**)::<lambda()>’ to ‘Callback {aka int (*)()}’ for argument ‘1’ to ‘void legacy(Callback)’
 legacy([&]() { return number; });

我该如何解决这个问题?或者技术上不可能使用捕获lambda作为C函数指针?

2 个答案:

答案 0 :(得分:9)

不,如果它捕获了任何东西,你就无法将lamdba转换为函数指针。

C ++标准,第5.1.2 / 6节:[expr.prim.lambda],强调我的:

  

非通用lambda表达式没有lambda-capture的闭包类型具有公共非虚拟非显式const转换函数,指向函数,具有C ++语言链接(7.5) )具有与闭包类型的函数调用操作符相同的参数和返回类型。此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用操作符具有相同的效果

答案 1 :(得分:5)

函数指针就是:指向某些代码位的指针。没有数据。

带有捕获的lambda也需要捕获的数据才能产生有用的结果。

没有办法可以自动运作。但是,根据回调函数的调用方式,您可以解决它。您的legacy函数不会将回调保存在任何位置,它只会在自己执行期间调用它。此外,您还没有使用线程或类似的东西。只要这些假设成立,您就可以解决问题:

#include <cstdio>
#include <functional>

typedef int (*Callback)();

void legacy(Callback callback) {
    printf("%i\n", callback());
}

// Declared as void * outside legacyWrapper, instead of T * inside legacyWrapper,
// because no more than one variable is needed, no matter how many instantiations of
// legacyWrapper there are.
static void *legacyWrapperCallback;

template <typename T>
void legacyWrapper(T callback) {
    legacyWrapperCallback = &callback;
    legacy([]() -> int { return (*static_cast<T *>(legacyWrapperCallback))(); });
}

int stdCallback() {    
    return 1; 
}

int main(int argc, char* argv[]) {
    int number = 3;    

    // Standard C callback works
    legacy(stdCallback);

    // Lambda without capturing works
    legacy([]() { return 2; });    

    // Lambda with capturing doesn't work...
    //legacy([&]() { return number; });

    // ...unless the C++ wrapper is used
    legacyWrapper([&]() { return number; });

    return 0;
}