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。解決策の 1 つは、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」パラメーターからインスタンスへのポインターを取得する方法が必要です。このドキュメントの「void* uv_handle_t.data」ポインターを使用することをお勧めしますhttp://docs.libuv.org/en/latest/handle.html#c.uv_handle_t

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 では、おそらく代わりにラムダを使用できます。

于 2014-11-12T18:52:45.210 に答える
1

uv_listen()コールバック コネクタはstaticor free (クラス外) 関数を想定しています。

したがって、次のように関数を宣言する必要があります

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 に答える