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

3

5

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);
Noleta answered 12/11, 2014 at 18:35 Comment(3)
delete handles null-pointer by itself, your check is redundantEcclesiolatry
Can you show the actual uv_listen() signature please?Gainful
Sure, added it to the question.Noleta
E
12

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.

Ecclesiolatry answered 12/11, 2014 at 18:52 Comment(5)
Thank you @slava. Do you know of a workaround to use a non-static function reference? Because I'm planning to access a lot of instance variables from within the listen_uv_listen_uv_connection_cb function.Noleta
To elaborate more, uv_tcp_t* tcp holds a reference to the actual tcp connection. If I wanted to do something with that connection in listen_uv_listen_uv_connection_cb (which I definitely do) I would have to make uv_tcp_t* tcp static too, and if I make tcp static then I wouldn't be able to span new connections by making more new _tcp() instances. So that's the problem.Noleta
@almosnow no you do not have to make uv_tcp_t* tcp static, you get it as a first parameter in callback. See updated answerEcclesiolatry
Thanks @Slava, and thanks for the workaround. I had to accept the other answer because it gave me an insight onto why I can't use a regular function pointer in that particular case, and kind of feel bad because your answer is a really good one too. I will make use of the data property on uv_tcp_t to pass around my instance reference. Thanks again :D!Noleta
@almosnow when you work with C-API and provide a callback you always (if a library properly designed of course) get a cookie - usually a void pointer to user data directly or indirectly (as in this case), this is common method to use such callbacks in C++Ecclesiolatry
M
1

The uv_listen() call back connector expects a static or free (outside class) function.

Thus you should declare your function like this

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

Well, the static_cast<> actually requires your _tcp class inherits from uv_stream_t

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

To extend on your comment

"Could you please explain to me why does uv_listen expects a static function? Is this the behavior for all function pointer parameters?"

There's a difference made between class member function pointers, that need to be bound to a class instance for calling, and plain function pointers, that work for any function definition.

Why uv_listen() expects a plain function pointer, is hard to tell. May be because it's a native C-API (I actually don't know it), or for sake of flexibility.


NOTE: You should not use leading underscores for any symbols (as in class _tcp)!

Macaroni answered 12/11, 2014 at 19:6 Comment(1)
Thanks @πάντα ῥεῖ, could you please explain to me why does uv_listen expects a static function? Is this the behavior for all function pointer parameters?Noleta
A
0
void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
        printf("NEW CONNECTION\n");
    };        <<<<<remove ;

There should be no semicolon at the end of function definition.

And you should write constructor/copy ctr/assign operator for this class.

Anarthria answered 12/11, 2014 at 18:37 Comment(2)
I would say write or disableEcclesiolatry
Thanks for your observation @ravi, code still doesn't work though.Noleta

© 2022 - 2024 — McMap. All rights reserved.