non-member function pointer as a callback in API t

2019-05-12 20:49发布

I'm using an API that requires me to pass a function pointer as a callback. I'm trying to use this API from my class in C++ but I'm getting compilation errors.

The API definition is:

typedef void (__stdcall *STREAM_CALLBACK)(void *userdata);

__declspec(dllimport) int __stdcall set_stream_callback(
    STREAM_CALLBACK streamCB, void *userdata);

One example file, provided by the third party, is:

void __stdcall streamCB(void *userdata)
{
  // callback implementation
}

int main(int argc, const char argv[])
{
  int mid = 0;
  set_stream_callback(streamCB, &mid);
}

And that works fine. However when I try to use that in a class, I have an error:

error C3867: 'MyClass::streamCB': function call missing argument list; use '&MyClass::streamCB' to create a pointer to member

The suggestion to use

&MyClass::streamCB

doesn't work. I understood that the set_stream_callback only accepts a non-member function.

The problem is very similar to How can I pass a class member function as a callback? in which Johannes makes a concise suggestion, however I do not understand it very well. Could anyone expand a bit, if I am correct that it is relevant to this question?

I have tried:

void __stdcall MyClass::streamCB(void *userdata)
{
  // callback implementation
}

static void MyClass::Callback( void * other_arg, void * this_pointer ) {
    MyClass * self = static_cast<ri::IsiDevice*>(this_pointer);
    self->streamCB( other_arg );
}

//and in the constructor
int mid = 0;
set_stream_callback(&MyClass::Callback, &mid);

But

error C2664: 'set_stream_callback' : cannot convert parameter 1 from 'void (__cdecl *)(void *,void *)' to 'STREAM_CALLBACK'

How do I get around this?


Edit1: Also, I want to use userdata inside the streamCB callback.

2条回答
劫难
2楼-- · 2019-05-12 21:16

The idea of calling a member function from a callback taking only non-member functions is to create a wrapper for you member function. The wrapper obtains an object from somewhere and then calls the member function. If the callback is reasonably well designed it will allow you to pass in some "user data" which you'd use to identify your object. You, unfortunately, left out any details about your class so I'm assuming it looks something like this:

class MyClass {
public:
    void streamCB() {
         // whatever
    }
    // other members, constructors, private data, etc.
};

With this, you can set up your callback like so:

void streamCBWrapper(void* userData) {
    static_cast<MyClass*>(userData)->streamCB()
}

int main() {
    MyClass object;
   set_stream_callback(&streamCBWrapper, &object);
   // ...
}

There are various games you can play with how to create the streamCBWrapper function (e.g., you can make it a static member of your class) but all come down to the same: you need to restore your object from the user data and call the member function on this object.

查看更多
冷血范
3楼-- · 2019-05-12 21:28

You can achieve what you want to do by turning the userdata into a property of MyClass. Then you don't have to pass it to MyClass::Callback, which would be impossible, since you can only pass one parameter, and it would be the object instance. Here's an example.

void __stdcall MyClass::streamCB()
{
  // callback implementation
}

static void MyClass::Callback(void * this_pointer ) {
    MyClass * self = static_cast<MyClass>(this_pointer);
    self->streamCB();
}

MyClass::MyClass(void *userdata) {
    // do whatever you need to do with userdata
    // (...)
    // and setup the callback at C level
    set_stream_callback(&MyClass::Callback, (void *)this);
}

In your example, the int mid variable would become a property of that class, and thus be accessible from the callback implementation streamCB.

查看更多
登录 后发表回答