5

我正在尝试创建一个类来抽象 libuv 网络功能的一些基本行为。

#define TCP_BACKLOG 256
class _tcp {
    uv_tcp_t* tcp = NULL;
    public:
    ~_tcp() { delete tcp; }
    void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
        printf("NEW CONNECTION\n");
    }
    void listen(const char* host, int port) {
        tcp = new uv_tcp_t();
        uv_tcp_init(uv_default_loop(), tcp);
        sockaddr_in* addr = new sockaddr_in();
        uv_ip4_addr(host, port, addr);
        uv_tcp_bind(tcp, (const sockaddr*)addr, 0);
        delete addr;

        uv_listen((uv_stream_t*)tcp, TCP_BACKLOG, listen_uv_listen_uv_connection_cb);
    }
};

前面显示的代码的问题是,当我尝试编译它时,我收到以下错误:

error: reference to non-static member function must be called
  on: uv_listen((uv_stream_t*)tcp, TCP_BACKLOG, listen_uv_listen_uv_connection_cb);
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

它指出listen_uv_listen_uv_connection_cb了罪魁祸首。

有人可以向我解释一下,为什么这是一个错误,我应该如何解决它?

uv_listen()签名uv_connection_cb声明如下

UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb);
typedef void (*uv_connection_cb)(uv_stream_t* server, int status);
4

3 回答 3

11

即使具有相同的签名,您也无法将非静态成员函数转换为指向函数的指针,因为从技术上讲,成员函数有一个名为this. 解决方案之一是使listen_uv_listen_uv_connection_cb静态:

class _tcp {
    uv_tcp_t* tcp = NULL;
    public:
    ~_tcp() { delete tcp; }
    static void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
        printf("NEW CONNECTION\n");
    }
    void listen(const char* host, int port) {
        tcp = new uv_tcp_t();
        uv_tcp_init(uv_default_loop(), tcp);
        sockaddr_in* addr = new sockaddr_in();
        uv_ip4_addr(host, port, addr);
        uv_tcp_bind(tcp, (const sockaddr*)addr, 0);
        delete addr;

        uv_listen((uv_stream_t*)tcp, TCP_BACKLOG, 
                   &_tcp::listen_uv_listen_uv_connection_cb);
    }
};

PS 为了能够调用非静态方法,您需要一种方法来_tcp从“uv_stream_t* stream”参数中获取指向您的实例的指针。我建议使用此文档http://docs.libuv.org/en/latest/handle.html#c.uv_handle_t中的“void* uv_handle_t.data”指针

static void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
    _tcp *tcp = static_cast<_tcp *>( stream->data );
    tcp->regularMethod();
}

当然,您应该在初始化时分配this指针:uv_handle_t.datauv_tcp_t *

void listen(const char* host, int port) {
    tcp = new uv_tcp_t();
    uv_tcp_init(uv_default_loop(), tcp);
    tcp->data = this; // do not forget it
    ...
}

我会将此初始化代码移至构造函数。

对于要与此库一起使用的每个回调,您都需要这样的静态包装器。使用 c++11,您可能可以使用 lambda 代替。

于 2014-11-12T18:52:45.210 回答
1

uv_listen()回调连接器需要一个或static免费(外部类)函数。

因此,您应该像这样声明您的函数

static void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
    printf("NEW CONNECTION\n");
    _tcp* thisStream = static_cast<_tcp*>(stream);
}

好吧,static_cast<>实际上需要您的_tcp类继承自uv_stream_t

class _tcp : public uv_stream_t {
    // ...
};

扩展您的评论

“你能解释一下为什么 uv_listen 需要一个静态函数吗?这是所有函数指针参数的行为吗?”

类成员函数指针(需要绑定到类实例以进行调用)和普通函数指针(适用于任何函数定义)之间存在差异。

为什么uv_listen()需要一个普通的函数指针,很难说。可能是因为它是原生的 C-API(我其实不知道),或者是为了灵活性。


注意:您不应该对任何符号使用前导下划线(如class _tcp)!

于 2014-11-12T19:06:15.147 回答
0
void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
        printf("NEW CONNECTION\n");
    };        <<<<<remove ;

函数定义的末尾不应该有分号。

你应该为这个类编写构造函数/复制 ctr/assign 运算符。

于 2014-11-12T18:37:48.410 回答