jenny
jenny

Reputation: 11

C++ class member function to C struct function pointer

I'm trying to make C wrapper in C++.

But I can't convert a function within a class to a struct function pointer.

myCallback function shouldn't be used as static because it have to be overrided.

Here is sample code:

#include <iostream>

extern "C" {
 typedef struct _client_config
{
    void (*on_connect)(void *client, int result);
} client_config;
}

class Parent
{
public:
    Parent() {};
    ~Parent() {};

    virtual void myCallback(void* hello, int world) = 0;
};

class Child : public Parent
{
public:
    Child() {};
    ~Child() {};

    void myCallback(void* hello, int world)
    {
        printf("Hello World! \n");
    }
};

int main(void)
{
    Parent* myClass = new Child();

    client_config* myClientConfig = (client_config *)malloc(sizeof(client_config));

    myClientConfig->on_connect = myClass->myCallback;

    return 0;
}

Here is what the compiler says during compilation:

Error   1   error C3867: 'Parent::myCallback': function call missing argument list; use '&Parent::myCallback' to create a pointer to member
Error   2   error C2440: '=' : cannot convert from 'void (__thiscall Parent::* )(void *,int)' to 'void (__cdecl *)(void *,int)'

Can anyone point out a way to bind a class member function to a struct function pointer or what I am doing wrong here?

Upvotes: 1

Views: 893

Answers (1)

Ben Voigt
Ben Voigt

Reputation: 283634

A C-library's function pointer is compatible with only an extern "C" free function. (Even a static member function is not compatible, the language linkage is wrong).

You'll have to use such a function as a trampoline, ask the object which you receive a pointer to where your object is (the library does provide a way for you to store some form of user-data which can be retrieved inside a callback, doesn't it? Perhaps that client parameter is like this, you'll have to read the documentation.), cast to Parent*, and perform the member call. The compiler will not do this for you.

extern "C" void connect_trampoline( void* client, int result )
{
    Parent* my_object = static_cast<Parent*>(client);
    my_object->myCallback(result);
}

myClientConfig->on_connect = &connect_trampoline;

C++ libraries are usually coded to make this easier, for example by accepting a std::function which can carry state of its own. But in the C programming style, which C libraries use, you have to find your object and do an ugly cast before using members.

In case the library doesn't make some form of pointer under your control accessible to the callback, but does pass a pointer to the library object associated with the callback, you can use a global std::map<library_object*, Parent*> to lookup your object. This approach also avoids the cast.


Be careful that when you hand the pointer to your object to the library to hold for you, you give it the same exact type (Parent* in this case) that you will cast back later. If you give a Derived*, the effect is that of doing reinterpret_cast<Parent*>(p_derived) which is very bad. (static_cast to void* when giving the pointer to the library, static_cast to Parent* when you get it back, and the C++ standard says this sequence is exactly the same behavior as reinterpret_cast)

Upvotes: 1

Related Questions