Passing a qualified non-static member function as a function pointer

前端 未结 3 791
后悔当初
后悔当初 2020-12-11 07:38

I have a function in an external library that I cannot change with the following signature:

void registerResizeCallback(void (*)(int, int))

相关标签:
3条回答
  • 2020-12-11 07:42

    Check "[33.2] How do I pass a pointer-to-member-function to a signal handler, X event callback, system call that starts a thread/task, etc?" at the C++ FAQ Lite:

    Don't.

    Because a member function is meaningless without an object to invoke it on, you can't do this directly

    ...

    As a patch for existing software, use a top-level (non-member) function as a wrapper which takes an object obtained through some other technique.

    0 讨论(0)
  • 2020-12-11 07:42

    Expanding on Michael Burr's advice, you will have to find out how a non member function can gain access to the instance of the object you are modifying. One common way is to take advantage of the scope of static globals in C:

    // Top of your .c module:
    static Window *gMyWindow;
    
    // The declaration
    extern "C" {
      void* my_callback(int, int);
    }
    
    // Later, set it just before handing off the callback
    void somefunc() {
      ...
      gMyWindow = &windowObjectRef;
      registerResizeCallback(my_callback);
      windowObjectRef.SomeOtherWindowCallCausingCallbackInvoke();
      ...
    }
    
    // The callback in the same .c module as the global
    void my_callback(int x, int y) {
      Window *object = gMyWindow;
      object->Resize(x, y);
    }
    

    I have not compiled/run the above code, so there may be tweaks in the details, but hopefully the concept is clear: the callback must be a bridge between C and C++, and then the question is how to get your object "into" the callback to be the instance for the member function call.

    There may be other reasons in your environment why the global example above won't work, so then your task would be to find out what other mechanism besides globals would allow you to pass the object into the callback based on your situation.

    0 讨论(0)
  • 2020-12-11 07:59

    As Igor Oks indicates, you can't do this. The remainder of this question is not so much an answer to your problem, but a discussion of how something like this should work with a properly designed callback API (it appears the one you're using isn't).

    Most well-designed callback interfaces let you provide a "void *" or some other way to get a context in the callback. A common way to use this with C++ is to pass an object pointer in the void * context parameter, then the callback function can cast it back into an object pointer and call the member method to do the real work. It's too bad the callback API you're using doesn't provide for context data.

    Strictly speaking, the callback must be extern "C", but using static member methods for callbacks is common and I think in practice there's never a problem. (This is assuming that the callback API is a C interface, which is by far the most common).

    An example:

    // callback API declaration's
    
    extern "C" {
        typedef unsigned int callback_handle_t;
    
        typedef void (*callback_fcn_t)( void* context, int data1, int data2);
    
        callback_handle_t RegisterCallback( callback_fcn_t, void* context);
        void UnregisterCallback( callback_handle_t);
    }
    
    // ----------------------------------
    
    // prototype for wrapper function that will receive the callback and 
    //  transform it into a method call
    
    extern "C" 
    static void doWorkWrapper( void* context, int data1, int data2);
    
    
    // the class that does the real work
    
    class worker {
    public:
        worker() {
            hCallback = RegisterCallback( doWorkWrapper, this);
        }
    
        ~worker() {
            UnregisterCallback( hCallback);
        }
    
        void doWork( int data1, int data2) {
            // ... 
        };
    
    private:
        callback_handle_t hCallback;
    };
    
    // the wrapper that transforms the callback into a method call
    extern "C" 
    static void doWorkWrapper( void* context, int data1, int data2)
    {
        worker* pWorker = static_cast<worker*>( context);
    
        pWorker->doWork( data1, data2);
    }
    
    0 讨论(0)
提交回复
热议问题