2014-11-12 16 views
5

Я пытаюсь создать класс, чтобы абстрагироваться от базового поведения сетевых функций libuv.Ошибка C++: ссылка на нестационарную функцию-член должна быть вызвана

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

удалить ручки нулевой указатель сам по себе, ваш чек лишний – Slava

+0

@Slava Спасибо, не знал об этом. – almosnow

+1

Можете ли вы показать фактическую подпись 'uv_listen()', пожалуйста? –

ответ

8

Вы не можете преобразовать нестатические функции-члены в указатель на функцию даже с той же сигнатурой, поскольку технически функция-член имеет скрытый параметр, называемый 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 поток» параметра. Я хотел бы предложить использовать «недействительный * 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.data при инициализации 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 
    ... 
} 

и я хотел бы переместить этот код инициализации для конструктора.

Вам понадобится такая статическая оболочка для каждого обратного вызова, который вы собираетесь использовать с этой библиотекой. С C++ 11 вы, скорее всего, можете использовать лямбда.

+0

Спасибо @slava. Вы знаете об обходном пути для использования ссылки на нестатические функции? Потому что я планирую получить доступ к множеству переменных экземпляра из функции 'listen_uv_listen_uv_connection_cb'. – almosnow

+0

Чтобы уточнить, 'uv_tcp_t * tcp' содержит ссылку на фактическое соединение tcp. Если бы я хотел что-то сделать с этим соединением в 'listen_uv_listen_uv_connection_cb' (что я определенно делаю), мне тоже пришлось бы ставить' uv_tcp_t * tcp' статичный, и если я стану ставить 'tcp', то я бы не смог охватить новый подключений, создавая больше экземпляров 'new _tcp()'. Вот в чем проблема. – almosnow

+0

@almosnow no вам не нужно ставить 'uv_tcp_t * tcp' static, вы получаете его как первый параметр в обратном вызове. См. Обновленный ответ – Slava

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

Там не должно быть точкой с запятой в конце определения функции.

И вы должны написать оператор constructor/copy ctr/assign для этого класса.

+0

Я бы сказал, напишите или отключите – Slava

+1

Спасибо за ваше наблюдение @ravi, код все равно не работает. – almosnow

1

Контактный разъем обратного вызова 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 { 
    // ... 
}; 

Продлить на 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?"

Существует разница между указателями функций класса, которые должны быть привязаны к экземпляру класса для вызова, и простые указатели функций, которые работают для определения любой функции.

Почему uv_listen() ожидает простой указатель функции, трудно сказать. Может быть, потому, что это родной C-API (я на самом деле этого не знаю) или ради гибкости.


ПРИМЕЧАНИЕ: Вы не должны использовать ведущие подчеркивания для любых символов (как в class _tcp)!

+0

Спасибо @ πάντα ῥεῖ, не могли бы вы объяснить мне, почему uv_listen ожидает статическую функцию? Это поведение для всех параметров указателя функции? – almosnow

Смежные вопросы