1
// 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);

  //...
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
duong_dajgja
  • 4,196
  • 1
  • 38
  • 65
  • You really can't use member functions as pointers to non-member functions, no matter how you spin it. The only workaround is to use a `static` member function that gets a pointer to the object as an argument (or as a member of a structure passed as argument) and then call the non-static member function. – Some programmer dude Oct 15 '18 at 15:18
  • I think the third parameter is a ”user data” pointer where you can pass a pointer to your object. – molbdnilo Oct 15 '18 at 15:52

1 Answers1

2

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.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I'd worry about the lifetime of that lambda. But otherwise, I agree with your "general" approach here. – Kevin Anderson Oct 15 '18 at 16:01
  • 1
    @KevinAnderson No need to worry. The lambda doesn't capture anything. – eerorika Oct 15 '18 at 16:05
  • @duong_dajgja A leftover from an earlier version which was unnecessarily complex. I've fixed it now. – eerorika Oct 15 '18 at 16:08
  • oh this way I can just bypass `cbdata` in `server.get_handler_member(conn, cbdata);` -> `server.get_handler_member(conn, nullptr);` right? – duong_dajgja Oct 15 '18 at 16:09
  • @duong_dajgja Why not remove the argument from the member function completely? You *could* pass more data than just `this` in the pointer, but a better alternative is probably to keep any data as members of `HttpServer`. – eerorika Oct 15 '18 at 16:12
  • I can make `get_handler_free` a friend function of `HttpServer` class then it can freely access the private `get_handler_member`. Interesting! – duong_dajgja Oct 15 '18 at 16:28