将libuv与函数结构一起使用,而不是函数回调

Using libuv with functor structure instead of function callback

本文关键字:函数 回调 libuv 结构 一起      更新时间:2023-10-16

有人知道为什么在使用函子结构而不是实际函数作为回调时使用libuv-segfault吗。在下面的示例中,我创建了要使用的结构CB,而不是connection_CB。segfault发生在运算符()中。

#include <uv.h>
#include <stdio.h>
#include <stdlib.h>

uv_buf_t alloc_buffer(uv_handle_t * handle, size_t size);
void connection_cb(uv_stream_t * server, int status);
void read_cb(uv_stream_t * stream, ssize_t nread, uv_buf_t buf);
struct CB {
    State *state_;
    CB(State *state) : state_(state) {}
    void operator()(uv_stream_t *server, int status) {
        uv_tcp_t * client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t));
        /* if status not zero there was an error */
        if (status == -1) {
            printf("error 2n");
        }
        /* initialize the new client */
        uv_tcp_init(loop, client);
        /* now let bind the client to the server to be used for incomings */
        if (uv_accept(server, (uv_stream_t *) client) == 0) {
            /* start reading from stream */
            int r = uv_read_start((uv_stream_t *) client, (uv_alloc_cb)alloc_buffer, read_cb);
            if (r) {
                printf("error 3n");
            }
            state_->onConnect();
        } else {
            /* close client stream on error */
            uv_close((uv_handle_t *) client, NULL);
        }
    }
};
CB cb;
uv_tcp_t server;
uv_loop_t * loop;
int main() {
    loop = uv_default_loop();
    /* convert a humanreadable ip address to a c struct */
    struct sockaddr_in addr;
    uv_ip4_addr("127.0.0.1", 3005, &addr);
    /* initialize the server */
    uv_tcp_init(loop, &server);
    /* bind the server to the address above */
    uv_tcp_bind(&server, (const struct sockaddr *)&addr, 0);
    /* let the server listen on the address for new connections */
    int r = uv_listen((uv_stream_t *) &server, 128, (uv_connection_cb)&cb);
    if (r) {
        printf("error 1n");
        return -1;
    }
    /* execute all tasks in queue */
    return uv_run(loop, UV_RUN_DEFAULT);
}

/**
 * Callback which is executed on each new connection.
 */
void connection_cb(uv_stream_t * server, int status) {
    /* dynamically allocate a new client stream object on conn */
    uv_tcp_t * client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t));
    /* if status not zero there was an error */
    if (status == -1) {
        printf("error 2n");
    }
    /* initialize the new client */
    uv_tcp_init(loop, client);
    /* now let bind the client to the server to be used for incomings */
    if (uv_accept(server, (uv_stream_t *) client) == 0) {
        /* start reading from stream */
        //int r = uv_read_start((uv_stream_t *) client, (uv_alloc_cb)alloc_buffer, read_cb);
        int r = 0;
        if (r) {
            printf("error 3n");
        }
    } else {
        /* close client stream on error */
        uv_close((uv_handle_t *) client, NULL);
    }
}
/**
 * Callback which is executed on each readable state.
 */
void read_cb(uv_stream_t * stream, ssize_t nread, uv_buf_t buf) {
    /* dynamically allocate memory for a new write task */
    uv_write_t * req = (uv_write_t *) malloc(sizeof(uv_write_t));
    /* if read bytes counter -1 there is an error or EOF */
    if (nread == -1) {
        printf("error 4n");
        uv_close((uv_handle_t *) stream, NULL);
    }
    /* write sync the incoming buffer to the socket */
    int r = uv_write(req, stream, &buf, 1, NULL);
    if (r) {
        printf("error 5n");
    }
}
/**
 * Allocates a buffer which we can use for reading.
 */
uv_buf_t alloc_buffer(uv_handle_t * handle, size_t size) {
        return uv_buf_init((char *) malloc(size), size);
}

粘贴的代码将不起作用,因为libuv需要uv_listen中的函数指针——您提供的是指向结构的指针。这个结构恰好有一个operator(),但这并不能使结构的地址成为处理器可以跳转到并执行代码的地址。operator()与结构的任何其他方法一样,但您可以简单地使用().operator()进行调用,以提高代码的可读性。此外,由于operator()是非静态的,它期望对this的隐式引用,而libuv不会提供,因为它不期望必须提供。

为了实现您想要做的事情,您应该提供一个普通的C函数回调,并将额外的上下文数据存储在句柄的.data字段中:

代替:

    int r = uv_listen((uv_stream_t *) &server, 128, (uv_connection_cb)&cb);

用途:

    server.data = &cb;
    int r = uv_listen((uv_stream_t *) &server, 128, [](uv_stream_t *server, int status){
        (*(CB*)server->data)(server, status);
    });

uv控制柄上的.data字段正是为此目的而提供的。