【问题标题】:Passing c++ lambda to C functions将 c++ lambda 传递给 C 函数
【发布时间】:2016-06-12 06:14:47
【问题描述】:

我正在尝试在 libuv 上封装一个 C++ 层,并使用 lambda 作为回调函数。但是 gcc 出错了。

这是缩小版:

#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;

libuv中的相关声明为:

#   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);

g++ 给出的错误:

$ 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)’        
 }));

这里到底出了什么问题?有什么办法可以使这项工作?

更新:

更有趣 .. 这个在 lambda 的主体中做了一些事情;第一个电话有效,第二个电话无效。

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(); });

【问题讨论】:

    标签: c++ lambda libuv


    【解决方案1】:

    捕获的 lambda 不能转换为函数指针,只有非捕获的可以:

    //Lambda captures 'this', and so cannot be converted to function pointer
    [this](uv_stream_s *server, int status) -> void {
           this->on_conn(server,status);
        }
    

    【讨论】:

    • 好的,谢谢! .. .. lambda 不会产生 thunk ... 可以理解。
    • @Vardhan 一个 lambda 可以 转换为函数,但前提是它不捕获任何内容:)
    • hmmm .. 即使 std::bind() 也不起作用,尝试了几种变体;看起来将不得不使用 libuv 提供的“数据”字段来填充它。
    【解决方案2】:

    也许您可以使用类似以下的技巧:

    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;
    };
    

    一旦发生某些事情,流就会被归还,它的句柄只不过是一个裸指针。
    您可以使用相同的流来存储控制器的信息(在您的情况下为 Test 类的实例),并在收到流时将其转换为原始形式。

    否则,请使用作为句柄一部分的 data 字段(如果它仍未使用)。

    【讨论】:

      【解决方案3】:

      应该可以了。我有同样的问题,所以我通过句柄对象属性将当前对象发送到 lambda。

         #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;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多