C++ Function Callbacks: Cannot Convert from a Member Function to a Function Signature

C++: passing the template callback method into the other method: cannot convert ‘unresolved overloaded function type to pointer

You must qualify the name of a non-static member with the class name explicitly when constructing a pointer to member.

(emphasis mine)


  1. If the operand is a qualified name of a non-static member, e.g.
    &C::member, the result is a prvalue pointer to member function or
    pointer to data member of type T in class C. Note that neither &member
    nor C::member nor even &(C::member) may be used to initialize a
    pointer to member
    .

E.g.

store_subflow_bunch_on_open(
handle(response_msg.file_id), response_msg.msg_id,
static_cast<CIFS_RESOURCE_TYPE>(parm_open2_rsp->resource_type),
&smb1_subdecoder::send_subflow_premature_end_msg<ntdec_cifs_close_request>,
// ^^^^^^^^^^^^^^^^^
&smb1_subdecoder::send_subflow_premature_end_msg<ntdec_cifs_close_response>);
// ^^^^^^^^^^^^^^^^^

Using a C++ class member function (cannot be static) as a C callback function

It is not possible, at least with that handler_t signature.

While you can create a free function on your .cpp to wrap the member call, you need a pointer to the Foo instance:

void my_wrap(int foo, void* bar) {
Foo* some_foo_instance = ...;
some_foo_instance->handler(foo, bar);
}

int main(int argc, char **argv) {
set_handler(&my_wrap);
}

You need some void* to pass the Foo instance as a handler attribute:

// Header
typedef void (*handler_t)(int foo, void *bar, void* arg1);
void set_handler(handler_t h, void* arg1);

// Impl.
void set_handler(handler_t h, void* arg1) {
handler_ = h;
handler_arg1_ = arg1;
}

// cpp
void my_wrap(int foo, void* bar, void* arg1) {
Foo* some_foo_instance = static_cast<Foo*>(arg1);
some_foo_instance->handler(foo, bar);
}

// main
int main(int argc, char **argv) {
Foo some_concrete_instance;
set_handler(&my_wrap, static_cast<void*>(&some_concrete_instance));
}

C++ Member function as a callback

There are two problems:

  • std::mem_fn(&app::callback) takes an instance of app as the first parameter.
  • The most idiomatic alternative would be to use a capturing lambda such as
    [this] (Json::Value& json) { return callback(json); }
    but these cannot be converted to function pointers, which makes implementing generic containers for storing and dynamically dispatching to them extremely difficult to do.

One possible approach is to amend your websocket interface and accept an optional void* context parameter like this:

using cb = int(*)(Json::Value &json, void* ctx);

static void websocket::listen(cb callback, const char* address, void* ctx = nullptr)
{
...
}

int app::callback(Json::Value& json)
{
return db->insert(json);
}

void app::start()
{
websocket::listen([] (Json::Value &json, void* ctx) -> int {
return static_cast<app*>(ctx)->callback(json);
}, path, this);
}

If you prefer more type safety for this erasure, you can consider std::any:

using cb = int(*)(Json::Value &json, std::any ctx);

static void websocket::listen(cb callback, const char* address, std::any ctx = {})
{
...
}

int app::callback(Json::Value& json)
{
return db->insert(json);
}

void app::start()
{
websocket::listen([] (Json::Value &json, const std::any& ctx) -> int {
return std::any_cast<app*>(ctx)->callback(json);
}, path, this);
}

The std::any_cast will throw a std::bad_any_cast if ctx was empty or storing a pointer that wasn't a type erased app*.

Passing a member function as standard function callback

Member functions take *this pointer as a first parameter, so your function signature is actually:

void(HouseKeeper*, char*, uint8_t*, unsigned int)

While std::function in library setCallback function takes:

std::function<void(char*, unsigned char*, unsigned int)>.

You have to change your uint8_t* to unsigned char* in your callback second parameter (thanks Daniel H), and also get rid of implicit *this.

  • You can use std::bind to bind *this pointer to match setCallback() signature:

    std::function<void(char*, uint8_t*, unsigned int)> yourFunction = std::bind(&HouseKeeper::callback, this, _1, _2, _3);

    library.setCallback(yourFunction);
  • Or wrap your call in lambda function:

    std::function<void(char*, uint8_t*, unsigned int)> yourFunction = [=](char* topic, uint8_t* payload, unsigned int length) {
    this->callback(topic, payload, length);
    }

    library.setCallback(yourFunction);

Conversion from class member function to C-like function pointer

i would like to pass directly to cURL a member function of a given class, using something like std::bind or std::mem_fn, but the result of these templates cannot be assigned to a C function pointer, as required in my case. Is there an elegant solution to this issue?

The solution you already have, using a static function (or a free function), is the only option.

How to use a C++ member function as the callback function for a C framework

Does callback function need to be declared under extern "C"?

NO. extern "C" is necessary only when you are calling a C++ function directly, without the use of function pointers, from C. If function pointers are used, extern "C" is not required.

Can I use non-static member functions as a callback?

NO. Non-static member functions of class A have an implicit first parameter corresponding to this pointer.

Can I use static member functions as a callback?

YES, as long as signature matches with that of the callback.

Does it matter if the function is a template function or not?

NO, template function can be used as callbacks as long as the signature of the instantiated template matches with the callback.

Using a C++ class member function as a C callback function

You can do that if the member function is static.

Non-static member functions of class A have an implicit first parameter of type class A* which corresponds to this pointer. That's why you could only register them if the signature of the callback also had the first parameter of class A* type.

Callback to a member function

You cannot use a non-static member function as a callback parameter that expects a regular function, because their signatures are incompatible:

  • A non-member or a static member function takes only the parameters in its signature
  • A non-static member function takes an additional "hidden" parameter for the object on which it is called.

The common workaround for this is possible only if the library that performs a callback lets you pass custom parameters with the callback registration. This is usually done with a void* pointer, which you pass to the library when you register the callback, and then the library passes it back to you when it calls back the callback function.

Here is how:

// This is the static function that you register for your callback
static void staticContinueAfterDESCRIBE(RTSPClient *client, ...) {
static_cast<MyRTSPClient*>(client)-> continueAfterDESCRIBE(client, ...);
}

Now that the function is static, you have no problem registering it as a callback. Once the function gets the control, it casts client to your MyRTSPClient* class, and performs the non-static callback.

How can I pass a class member function as a callback?

That doesn't work because a member function pointer cannot be handled like a normal function pointer, because it expects a "this" object argument.

Instead you can pass a static member function as follows, which are like normal non-member functions in this regard:

m_cRedundencyManager->Init(&CLoggersInfra::Callback, this);

The function can be defined as follows

static void Callback(int other_arg, void * this_pointer) {
CLoggersInfra * self = static_cast<CLoggersInfra*>(this_pointer);
self->RedundencyManagerCallBack(other_arg);
}


Related Topics



Leave a reply



Submit