Search code examples
c++libuv

C++ error: reference to non-static member function must be called


I'm trying to create a class to abstract some basic behavior of libuv's networking functions.

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

The problem with the previously shown code is that when I try to compile it I get the following error:

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

And it points to listen_uv_listen_uv_connection_cb as the culprit.

Can someone explain to me, why is that an error, and how am I supposed to fix it?

The uv_listen() and uv_connection_cb signatures are declared as follows

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

Solution

  • You cannot convert non-static member function to a pointer to function even with the same signature, as technically member function has a hidden parameter called this. One of the solution is to make listen_uv_listen_uv_connection_cb static:

    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 to be able to call a non-static method you would need a way to get a pointer to your _tcp instance from "uv_stream_t* stream" parameter. I would suggest to use "void* uv_handle_t.data" pointer from this doc 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();
    }
    

    Of course you should assign this pointer to uv_handle_t.data when you initialize uv_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
        ...
    }
    

    and I would move this initialization code to constructor.

    You would need such static wrapper for every callback you are going to use with this library. With c++11 you probably can use lambda instead.