最近在看libuv(一个纯C实现的多平台异步io库)的相关东西,里面有许多回调函数。例如:
int uv_listen(uv_stream_t *stream, int backlog, uv_connection_cb cb)
Start listening for incoming connections. backlog indicates the number of connections the kernel might queue, same as listen(2). When a new incoming connection is received the uv_connection_cb callback is called.
typedef void (*uv_connection_cb)(uv_stream_t *server, int status)
Callback called when a stream server has received an incoming connection. The user can accept the connection by calling uv_accept(). status will be 0 in case of success, < 0 otherwise.
即uv_listen
函数监听的端口有事件到来的时候,会主动调用一个uv_connection_cb
类型的回调函数,不过当时的需求是在调用uv_listen
方法的时候有一个对象需要传递到回调uv_connection_cb
回调中去(后来才知道,所有的handle
对象都有一个data
成员,可以通过这个东西传递),所以第一个想到的方法是使用带捕获的lambda额外增加一个参数,不过会报一个编译错。为了分析编译错原因,简单写了一段代码,方便观察原因。
// lambda.cpp
#include <iostream>
typedef void (*func)(int arg);
void test_func(int arg1, int arg2, int arg3)
{
std::cout << arg1 << arg2 << arg3 << std::endl;
}
void call_test_func(func fa)
{
int call_arg = 5;
fa(call_arg);
}
int main(int argc, char* argv[])
{
int main_arg1 = 1;
int main_arg2 = 2;
int main_arg3 = 3;
auto lam1 = [](int arg1, int arg2, int arg3)->void{test_func(arg1, arg2, arg3);};
auto lam2 = [main_arg1, main_arg2](int arg3)->void{test_func(main_arg1, main_arg2, arg3);};
lam1(main_arg1, main_arg2, main_arg3);
lam2(main_arg3);
// call_test_func(lam2);
// auto lam = [](int arg1)->void{ std::cout << arg1 << std::endl; };
// lam(0);
// call_test_func(lam);
return 0;
}
代码很简单,定义了两个函数test_func
和call_test_func
,不过call_test_func
的参数是一个接受一个整数参数无返回值的函数指针。和我在调用libuv方法时遇到的情景类似,而后我刚开始想到的解决办法是利用lambda的捕获构造一个接受一个整数的对象,而后利用捕获将main中的对象带到回调中。不过会报一个编译错(把注释掉的那行call_test_func(lam2)
恢复)。编译错如下:
和编译libuv时报的错类似,不能把某个类型的lambda转换为某种形式的指针。
so,先注释掉那行代码然后看一下调用带捕获的lambda和不带捕获的lambda的汇编码的差别:
g++ lambda.cpp -m32 -c
objdump -d lambda.o
-m32
是将lambda.cpp编译成32为程序,需要先下载32位的开发环境。接着看汇编内容:
<main>
000000ac <main>:
ac: 8d 4c 24 04 lea 0x4(%esp),%ecx
b0: 83 e4 f0 and