0%

C++ lambda interact with C's Callback function

從最簡單的情況說起

煤捕捉任何狀態的lambda可以直接轉化成普通的C function型態

1
2
3
4
5
6
7
8
typedef void(*callback)();
void testFunc(callback cb)
{
cb();
}
testFunc([] {
cout << "Lambda function" << endl;
});

帶狀態的情形

通常callback還會帶一個void *參數,可以讓你上下其手

1
2
3
4
5
6
7
8
9
10
typedef void(*callback)(void *);
void testFunc(callback cb, void *user_data)
{
cb(user_data);
}
int v = 123;
testFunc([](void *user_data) {
int *v = static_cast<int *>(user_data);
cout << "Lambda function " << *v<< endl;
}, &v);

當需要更多上下文時,需要自行定義structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// local variables
int x = 0;
float y = 1;

// locally defined uncopyable, unmovable type
struct MtEverest
{
MtEverest() = default;
MtEverest(const MtEverest& that) = delete; // no copy
MtEverest(const MtEverest&& that) = delete; // no move
} mt_everest;

// create "user-data" payload
auto payload = std::tie(x, y, mt_everest);
testFunc([](void *user_data) {
auto& payload_tup = *reinterpret_cast<decltype(payload)*>(user_data);
auto& xx = std::get<0>(payload_tup);
auto& yy = std::get<1>(payload_tup);
auto& me = std::get<2>(payload_tup);
}, &payload);

更高明的解法

當C API沒辦法有void *的指標讓你上下其手,或是packing/unpacking的程式碼讓人不耐
可以考慮以下這個解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename Lambda>
static auto lambdacb(Lambda &&l)
{
thread_local auto* p = &l; // initial assignment, allows using auto
p = &l;
return []() { return (*p)(); };
}

typedef void(*callback)();
void testFunc(callback cb)
{
cb();
}
// local variables
int x = 0;
float y = 1;

testFunc(lambdacb([&x, &y] {
cout << x << " " << y << endl;
}));

Reference

Lambda Magic
Lambda Callbacks
Technical Debt