2014-11-12 50 views
5

Próbuję utworzyć klasę, aby wykreślić podstawowe zachowanie funkcji sieciowych libuv.Błąd C++: odwołanie do niestatycznej funkcji składowej musi być nazywane

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

Problem z wcześniej przedstawionym kodzie jest to, że gdy próbuję go skompilować otrzymuję następujący błąd:

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

I to wskazuje na listen_uv_listen_uv_connection_cb jako sprawcy.

Czy ktoś może mi wyjaśnić, dlaczego jest to błąd i jak mam go naprawić?

W uv_listen() i uv_connection_cb podpisy są zadeklarowane w następujący sposób

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

kasowanie uchwytów zerowych samoistnie, czek jest zbędny – Slava

+0

@Sława Dzięki, nie wiedziałem o tym. – almosnow

+1

Czy możesz pokazać rzeczywistą sygnaturę 'uv_listen()'? –

Odpowiedz

8

Nie można przekształcić niestatycznej funkcji składowej w wskaźnik, aby działała nawet z tą samą sygnaturą, ponieważ technicznie funkcja pręta ma ukryty parametr o nazwie this. Jednym z roztworu jest, aby listen_uv_listen_uv_connection_cb statyczny:

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, aby móc wezwać non-statycznej metody potrzebny jest sposób, aby uzyskać wskaźnik do instancji _tcp z „uv_stream_t * stream” parametru. Proponuję użyć "void * uv_handle_t.data" wskaźnik z tego 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(); 
} 

Oczywiście należy przypisać this wskaźnik uv_handle_t.data kiedy zainicjować 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 
    ... 
} 

i chciałbym przenieść tę kod inicjalizacyjny do konstruktora.

Potrzebowałbyś takiego statycznego opakowania dla każdego wywołania zwrotnego, którego będziesz używać w tej bibliotece. W przypadku C++ 11 prawdopodobnie można użyć wartości lambda.

+0

Dziękuję @ slava. Czy znasz sposób obejścia, aby użyć niestatycznego odwołania do funkcji? Ponieważ planuję uzyskać dostęp do wielu zmiennych instancji z poziomu funkcji 'listen_uv_listen_uv_connection_cb'. – almosnow

+0

Aby opracować więcej, 'uv_tcp_t * tcp' zawiera odniesienie do rzeczywistego połączenia TCP. Jeśli chciałem zrobić coś z tym połączeniem w 'listen_uv_listen_uv_connection_cb' (co definitywnie robię) też musiałbym uczynić' uv_tcp_t * tcp' static, i jeśli zrobię 'tcp' static, wtedy nie byłbym w stanie rozpiąć nowego połączenia, tworząc więcej instancji 'new _tcp()'. Więc to jest problem. – almosnow

+0

@almosnow no nie musisz robić 'uv_tcp_t * tcp' static, otrzymujesz go jako pierwszy parametr w wywołaniu zwrotnym. Zobacz zaktualizowaną odpowiedź – Slava

0
void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) { 
     printf("NEW CONNECTION\n"); 
    };  <<<<<remove ; 

Nie powinno być średnik na końcu definicji funkcji.

Należy napisać instrukcję konstruktora/kopiuj ctr/assign dla tej klasy.

+0

Powiedziałbym, pisać lub wyłączać – Slava

+1

Dzięki za obserwację @ravi, kod nadal nie działa. – almosnow

1

Łącze oddzwonienia oczekuje funkcji static lub bezpłatnej (klasa zewnętrzna).

Zatem należy zadeklarować swoją funkcję jak ten

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

dobrze, static_cast<> rzeczywiście wymaga klasa _tcp dziedziczy uv_stream_t

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

Aby przedłużyć na 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?"

Występuje różnica między wskaźnikami funkcji klasy klasy, które muszą być powiązane z instancją klasy dla wywoływania i prostymi wskaźnikami funkcji, które działają dla dowolnej definicji funkcji.

Dlaczego uv_listen() oczekuje, że prosty wskaźnik funkcji jest trudny do określenia. Może być, ponieważ jest to natywny C-API (właściwie go nie znam) lub ze względu na elastyczność.


UWAGA: Nie należy stosować wiodące podkreślenia dla wszelkich symboli (jak w class _tcp)!

+0

Dzięki @ πάντα ῥεῖ, czy mógłbyś wyjaśnić mi, dlaczego uv_listen oczekuje funkcji statycznej? Czy jest to zachowanie dla wszystkich parametrów wskaźnika funkcji? – almosnow

Powiązane problemy