【问题标题】:Using libuv with functor structure instead of function callback使用带有仿函数结构的 libuv 而不是函数回调
【发布时间】:2015-12-23 18:09:18
【问题描述】:

有谁知道为什么 libuv 在使用函子结构而不是实际函数作为回调时会出现段错误。在下面的示例中,我创建了结构 CB 来代替 connection_cb。段错误发生在 operator() 中。

#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 2\n");
        }

        /* 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 3\n");
            }
            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 1\n");
        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 2\n");
    }

    /* 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 3\n");
        }
    } 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 4\n");
        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 5\n");
    }
}

/**
 * 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);
}

【问题讨论】:

  • 我认为将仿函数传递给期望函数指针的 C API 是不正确的。您需要编写包装器来进行必要的转换。话虽如此,如果您使用的是 c++11,则可以使用“无捕获”的 lambda 函数。但是考虑到你的回调的大小,这没有意义,也不是正确的做法。
  • 之间,当你没有任何状态要维护,也没有在 STL 算法中调用它时,为什么要为此使用函子。它似乎也不可能成为内联的候选对象。
  • 我想这样做的原因是因为我需要函数中的一些上下文来调用我自己的不同回调。有没有其他方法可以在这个回调中提供一些上下文?
  • 我编辑了问题,使状态是明确的,即使它是一个占位符。
  • 我不熟悉 libuv API。理想情况下,此类框架为调用者提供与 void* 等效的东西来存储他们的信息。请寻找那个。否则,您需要执行 stackoverflow.com/questions/1840029/… 之类的操作,并按照 libuv 的预期匹配函数签名。

标签: c++ libuv


【解决方案1】:

粘贴的代码将不起作用,因为 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 字段正是为此目的而提供的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-03
    • 2014-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多