Search code examples
c++cpointerscallbackpointer-to-member

Accessing C-style callbacks inside C++ class


The following C-style API function is provided by PiGPIO library:

typedef void (*gpioAlertFuncEx_t)(int, int, uint32_t, void *);  // assumed  
int gpioSetAlertFuncEx(unsigned user_gpio, gpioAlertFuncEx_t f, void *userdata)

Basically it allows you to handle a pin state change via a callback function.

So far so good. The problem is wrapping this callback into a c++ class.

My approach is the following:

class Pin
{
public:
    Pin(_GpioPin)
    {
        gpioSetAlertFuncEx(_GpioPin, &PushButton::internal_gpio_callback, this );
    }

    void internal_callback_func(int pin, int level, uint32_t tick)
    {
        cout << "New level: " << pin << " " << level;
    }
}

The problem is that the callback function type differs (because it is non-static). And the error is prompted:

 error: cannot convert 'void (Pin::*)(int, int, uint32_t, void*) {aka void (Pin::*)(int, int, unsigned int, void*)}' to 'gpioAlertFuncEx_t {aka void (*)(int, int, unsigned int, void*)}' for argument '2' to 'int gpioSetAlertFuncEx(unsigned int, gpioAlertFuncEx_t, void*)'
  gpioSetAlertFuncEx(this->GpioPin, &Pin::internal_gpio_callback), this );

What is the trick? How to cast &PushButton::internal_gpio_callback to match required template?

Later edit: I don't want to make the callback method static.


Solution

  • A pointer to a member function is not the same as a pointer to a non-member function. The difference is that a member function needs an object to be called on, something which C can't handle.

    There are ways around that, especially in your case where you already pass this as the userdata pointer. The solution is to simply wrap the real member function within a static member function (as those can be passed as C callback functions).

    For example:

    class Pin
    {
    public:
        Pin(_GpioPin)
        {
            gpioSetAlertFuncEx(_GpioPin, &Pin::static_internal_callback_func, this );
        }
    
    private:
        static void static_internal_callback_func(int pin, int level, uint32_t tick, void* userdata)
        {
            static_cast<Pin*>(userdata)->internal_callback_func(pin, level, tick);
        }
    
        void internal_callback_func(int pin, int level, uint32_t tick)
        {
            cout << "New level: " << pin << " " << level;
        }
    };