Reputation:
I am trying to wrap a C++ layer over libuv, and using lambda for callback functions. However gcc is erroring out.
Here is the minified version:
#include <uv.h>
class Test {
public:
void on_conn(uv_stream_t *server, int status) { }
void test() {
uv_tcp_t server;
auto err = uv_listen((uv_stream_t*)&server,
100,
[this] (uv_stream_s *server, int status) -> void {
this->on_conn(server,status);
});
}
};
Test t;
The relevant declarations in libuv are:
# define UV_EXTERN /* nothing */
struct uv_stream_s { ... };
typedef struct uv_stream_s uv_stream_t;
typedef void (*uv_connection_cb)(uv_stream_t* server, int status);
UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb);
The error g++ is giving:
$ g++ --version
g++ (GCC) 6.1.1 20160501
<<--ERROR--{reformatted}-->>
t.cpp:15:7: error: cannot convert
‘Test::test()::<lambda(uv_stream_s*, int)>’ to
‘uv_connection_cb {aka void (*)(uv_stream_s*, int)}’
for argument ‘3’ to ‘int uv_listen(uv_stream_t*, int, uv_connection_cb)’
}));
What exactly is broken here ? Any way to make this work ?
UPDATE:
More fun .. this in body of lambda does something ; first call works, second doesn't.
int cfunc( void cb() );
class Test {
public:
void d(){}
void test() {
cfunc( [=] () {});
cfunc( [=] () { this->d(); });
//cfunc( [this] () { });
//cfunc( [&this] () { });
}
};
t.cpp:10:34: error: cannot convert ‘Test::test()::<lambda()>’ to ‘void (*)()’ for argument ‘1’ to ‘int cfunc(void (*)())’
cfunc( [=] () { this->d(); });
Upvotes: 0
Views: 745
Reputation: 812
It should be work. I had same problem, so I sent current object to lambda over handle object property.
#include <uv.h>
class Test {
public:
void on_conn(uv_stream_t *server, int status) { }
void test() {
uv_tcp_t server;
server.data = this;
auto err = uv_listen((uv_stream_t*)&server,
100,
[] (uv_stream_s *server, int status) -> void {
auto self = (Test*)server->data;
self->on_conn(server,status);
});
}
};
Test t;
Upvotes: 0
Reputation: 50540
Maybe you can use a trick like the following one:
class Test;
struct my_uv_tcp_t: uv_tcp_t {
Test *test;
};
class Test {
public:
Test(): server{} { server.test = this; }
void on_conn(uv_stream_t *server, int status) { }
static void cb(uv_stream_t *server, int status) {
auto srv = static_cast<my_uv_tcp_t*>(server);
srv->test->on_conn(server, status);
}
void test() {
auto err = uv_listen((uv_stream_t*)&server, 100, Test::cb);
}
private:
my_uv_tcp_t server;
};
The stream is given back once something happens on it and the handle of it is nothing more than a naked pointer.
You can use that same stream to store the information of the controller (the instance of the Test
class in your case) and cast the stream to its original form when you receive it.
Otherwise, use the data
field that is part of the handle if it's still unused.
Upvotes: 0
Reputation: 48938
A capturing lambda cannot be converted to a function pointer, only a non-capturing can:
//Lambda captures 'this', and so cannot be converted to function pointer
[this](uv_stream_s *server, int status) -> void {
this->on_conn(server,status);
}
Upvotes: 3