Search code examples
c++qtstdcall

Error converting void(__cdecl MyClass::*)() to void *


I am trying to link to an external library in my QT application. The external library has a header file with the following relevant code I'm trying to call:

 extern VGRABDEVICE_API bool V_AssignFrameSizeCallback(IGrabChannel* pChannel, void* pFunc);

In the demo C++ program provided, which has no problems compiling, the following relevant code is:

// in main.cpp    
void _stdcall MyFrameSizeCallback(T x) {
    do_stuff;
}

int main(int argc, char* argv[]) {
    IGrabChannel* pChannel0 = something;
    V_AssignFrameSizeCallback(pChannel0, MyFrameSizeCallback);
}

I am trying to incorporate this code into my QT application, but getting problems. In my mainwindow.cpp file:

void _stdcall MainWindow::MyFrameSizeCallback(T x) {
    do_stuff;
}

void MainWindow::someFunction() {
    IGrabChannel* pChannel0 = something;
    V_AssignFrameSizeCallback(pChannel0, &MainWindow::MyFrameSizeCallback);
}

The error I'm getting is:

error: C2664: 'bool V_AssignFrameSizeCallback(IGrabChannel *,void *)' :
cannot convert argument 2 from 'void (__cdecl MainWindow::* )(T)' to 'void *'
There is no context in which this conversion is possible

What do I need to do? Thanks.


Solution

  • You have two problems. First, void* is a data pointer, not a function pointer. According to the C++ standard, casting between the two is not expected to work. Some platforms provide a stronger guarantee... for example Windows GetProcAddress and *nix dlsym mix the two.

    Next, your &MainWindow::MyFrameSizeCallback is not a function pointer, it is a pointer-to-member-function. Calling it requires a MainWindow object, which the external library doesn't know anything about.

    You need to provide an ordinary function, not a member function, to the library. If you have some way to get ahold of the MainWindow* object pointer, you can then call its member function to do the real work. Sometimes the library provides a "context" parameter which is passed to your callback; that's a great place to put the object pointer. Otherwise, you'll need to store your MainWindow* in a global variable. Easy if you have just one, while if you have more than one you might go with std::map<IGrabChannel*, MainWindow*>.

    Code:

    MainWindow* MainWindow::the_window;
    
    void MainWindow::MyFrameSizeCallback(T x)
    {
        do_stuff;
    }
    
    void _stdcall MyFrameSizeCallbackShim(T x)
    {
        MainWindow::the_window->MyFrameSizeCallback(x);
    }
    
    void MainWindow::someFunction()
    {
        IGrabChannel* pChannel0 = something;
        the_window = this;
        V_AssignFrameSizeCallback(pChannel0, &MyFrameSizeCallbackShim);
    }
    

    If the parameter x isn't an IGrabChannel, change the map datatype and insertion logic accordingly. If the parameter x isn't some sort of unique predictable identifier, you may be limited to only doing callbacks to one MainWindow instance.