将lambdas传递给传统的回调

kay*_*ahr 7 c++ lambda c++11

我在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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

qua*_*dev 9

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

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

对于一个非通用λ-表达的闭合类型没有λ-捕获具有公共非虚拟非显式const的转换功能,以函数指针与C++语言联动装置(7.5),其具有相同的参数和返回类型为闭包类型的函数调用操作符.此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果


小智 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 当然,如果您想从不同的线程多次使用API​​,这可能会让您遇到麻烦.您可以使`legacyWrapperCallback`线程本地 - 但如果回调发生在与原始调用不同的线程上,那么这会让您遇到麻烦. (2认同)