Search code examples
c++function-pointerspointer-to-memberstdbind

How to pass a member function as callback param to a function that expects a `typedef-ed` free function pointer?


// typedef from library that I cannot change
typedef int (*mg_request_handler)(mg_connection *conn, void *cbdata);

// this free function is for testing 
int get_handler_free(struct mg_connection* conn, void* cbdata) {
  //...
}

// this member function is what I want to use
int HttpServer::get_handler_member(struct mg_connection* conn, void* cbdata) {
  //...
}

// inside this member function, the callback param is needed
void HttpServer::start() {
  //...

  // this way doesn't work
  mg_request_handler get_handler = std::bind(&HttpServer::get_handler_member, this);
  mg_set_request_handler(ctx_, "/get", get_handler, nullptr);

  // this way works well
  mg_request_handler get_handler = &get_handler_free;
  mg_set_request_handler(ctx_, "/get", get_handler, nullptr);

  //...
}

Solution

  • It is not possible to have a (non-member-) function pointer to a non-static member function. It is also not possible to point a function pointer to a bound function.

    Notice how the free function type has an argument void *cbdata. You haven't shown the documentation of the API that you use, but I would be willing to bet that the API follows a common idiom, and the third argument of mg_set_request_handler is also void *cbdata. If my assumption is correct, the same pointer that was passed to registration, will be passed to the handler later. It's purpose is to pass data - such as your HttpServer instance into the callback.

    For example:

    mg_set_request_handler(ctx_, "/get", [](mg_connection *conn, void *cbdata) {
        assert(cbdata);
        HttpServer& server = *static_cast<HttpServer*>(cbdata);
        server.get_handler_member(conn, cbdata);
    }, this);
    

    If get_handler_member has non-public access, then you'll need to use a static member function instead of the lambda that I used in my example. Also, the cbdata argument of get_handler_member is now probably useless and can be removed.

    Do remember to keep the HttpServer instance alive as long as the handler is registered.

    Also, to re-iterate: This relies on my assumption about the API that you've shown. Consult the documentation carefully.