2014-11-12 18 views
5

Tôi đang cố gắng tạo lớp để tóm tắt một số hành vi cơ bản của các chức năng mạng của libuv.Lỗi C++: tham chiếu đến hàm thành viên không tĩnh phải được gọi là

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

Vấn đề với mã hiển thị trước đó là khi tôi cố gắng để biên dịch nó tôi nhận được lỗi sau:

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

Và nó trỏ tới listen_uv_listen_uv_connection_cb là thủ phạm.

Ai đó có thể giải thích cho tôi, tại sao lỗi đó xảy ra và tôi phải sửa lỗi như thế nào?

Các uv_listen()uv_connection_cb chữ ký được khai báo như sau

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

xóa tay cầm con trỏ rỗng, kiểm tra của bạn là dư thừa – Slava

+0

@Slava Cảm ơn, không biết về điều đó. – almosnow

+1

Bạn có thể hiển thị chữ ký 'uv_listen()' thực tế không? –

Trả lời

8

Bạn không thể chuyển đổi hàm thành viên không tĩnh thành con trỏ để hoạt động ngay cả với cùng một chữ ký, vì hàm thành viên kỹ thuật có thông số ẩn được gọi là this. Một trong những giải pháp là làm cho listen_uv_listen_uv_connection_cb tĩnh:

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 để có thể gọi một phương thức không tĩnh bạn sẽ cần một cách để có được một con trỏ đến _tcp dụ của bạn từ "uv_stream_t * stream" tham số. Tôi sẽ đề nghị để sử dụng "void * uv_handle_t.data" con trỏ từ doc này 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(); 
} 

Tất nhiên bạn nên gán this con trỏ đến uv_handle_t.data khi bạn khởi 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 
    ... 
} 

và tôi sẽ di chuyển này mã khởi tạo cho hàm tạo.

Bạn sẽ cần trình bao bọc tĩnh như vậy cho mọi cuộc gọi lại mà bạn sẽ sử dụng với thư viện này. Với C++ 11, bạn có thể sử dụng lambda.

+0

Cảm ơn bạn @slava. Bạn có biết cách giải quyết để sử dụng tham chiếu hàm không tĩnh không? Bởi vì tôi đang lập kế hoạch truy cập rất nhiều biến mẫu từ bên trong hàm 'listen_uv_listen_uv_connection_cb'. – almosnow

+0

Để xây dựng thêm, 'uv_tcp_t * tcp' giữ tham chiếu đến kết nối tcp thực tế. Nếu tôi muốn làm một cái gì đó với kết nối đó trong 'listen_uv_listen_uv_connection_cb' (mà tôi chắc chắn làm) tôi sẽ phải làm cho' uv_tcp_t * tcp' tĩnh quá, và nếu tôi thực hiện 'tcp' tĩnh thì tôi sẽ không thể span mới các kết nối bằng cách tạo thêm các trường hợp 'new _tcp()'. Vì vậy, đó là vấn đề. – almosnow

+0

@almosnow không có bạn không phải thực hiện 'uv_tcp_t * tcp' tĩnh, bạn lấy nó làm tham số đầu tiên trong gọi lại. Xem câu trả lời cập nhật – Slava

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

Không nên có dấu chấm phẩy vào cuối định nghĩa hàm.

Và bạn nên viết toán tử khởi tạo/sao chép ctr/gán cho lớp này.

+0

Tôi có thể viết hoặc tắt – Slava

+1

Cảm ơn bạn đã quan sát @ravi, mã vẫn không hoạt động. – almosnow

1

Đầu nối gọi lại uv_listen() yêu cầu chức năng static hoặc miễn phí (bên ngoài lớp học).

Vì vậy bạn nên tuyên bố chức năng của bạn như thế này

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

Vâng, static_cast<> thực sự đòi hỏi lớp _tcp của bạn được thừa hưởng từ uv_stream_t

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

Để mở rộng trên 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?"

Có sự khác biệt giữa các con trỏ hàm thành viên lớp, cần phải được liên kết với một cá thể lớp để gọi và các con trỏ hàm đồng bằng, hoạt động với bất kỳ định nghĩa hàm nào.

Tại sao uv_listen() mong đợi một con trỏ hàm đơn giản, khó nói. Có thể vì đó là một C-API gốc (tôi thực sự không biết), hoặc vì lợi ích của sự linh hoạt.


LƯU Ý: Bạn không nên sử dụng dấu gạch hàng đầu cho bất kỳ biểu tượng (như trong class _tcp)!

+0

Thanks @ πάντα ῥεῖ, bạn có thể vui lòng giải thích cho tôi tại sao uv_listen mong đợi một chức năng tĩnh? Đây có phải là hành vi cho tất cả các tham số con trỏ hàm không? – almosnow

Các vấn đề liên quan