caffeine
caffeine

Reputation: 445

Reference to non-static member function must be called when processing callbacks inside a class

I have the following API code provided by PiGPIO library. Basically it allows you to set a callback function when the state of a pin was changed.

Normally the API looks like this:

typedef void (*gpio_callback_t)(int, int, uint32_t);
int gpioSetAlertFunc(int, gpio_callback_t)

And it can be called like this:

void callback_func(int pin, int NewLevel, uint32_t CurrentTicks)
{
    // Callback process
}

int main()
{
    // Setting up callback function
    gpioSetAlertFunc(10, callback_func)

    // Do stuff while callback will be called independently  
}

Now the code above works perfectly!


The big problem comes when I try to wrap this code in a C++ class. The class shall look like the following:

class Pin
{
public:
    Pin()
    {
        gpioSetAlertFunc(10, this->internal_gpio_callback) );
    }

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

The problem is that the reference to that function is not allowed as it is not a static function. For things to be more complicated, I can't just make the callback function static. That callback function will interact a lot with the rest of the class methods and it's internal variables.

Do you know a workaround for this? Or maybe a dirty hack using pointers or something?

Upvotes: 3

Views: 1663

Answers (2)

Max Langhof
Max Langhof

Reputation: 23681

The gpio library code has no concept of your Pin classes, but you cannot call your member callback function without knowing which Pin object to call it on. Thus, you need some (non-member) function that finds the Pin object to use in the function call.

It appears that this information comes in the form of the int pin parameter. Thus, you need a (free) function that knows about all your Pin objects and which pin number they have, finds the correct one and calls its callback function:

class Pin
{
public:
    explicit Pin(int pin);
    void internal_callback_func(int level, uint32_t tick);
private:
    int _pin;
};

// Alternatively a std::array, a std::map or similar.
std::vector<Pin> allPins;

void pinCallback(int pin, int level, uint32_t tick)
{
    Pin& pinObject = allPins[pin];
    pinObject.internal_callback_func(level, tick);
    // The Pin object presumably knows its own number.
}

Pin::Pin(int pin) : _pin(pin)
{
    gpioSetAlertFunc(_pin, pinCallback);
}

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

void mainOrSo()
{
    // Create some pins and store them in the vector. Each one registers the callback on construction.
    for (int i = 0; i < 16; ++i)
       allPins.emplace_back(i);
}

Upvotes: 1

n. m. could be an AI
n. m. could be an AI

Reputation: 119847

An unbroken C-style callback will accept a void* argument to pass arbitrary user-defined data. Since this one doesn't, its a bug in the library design. Raise an issue with the library authors.

To work around this issue you need to create a closure as a C-compatible function. There's no way to do that is standard C or C++. There are libraries (non-standard-conforming and not portable by necessity) that solve this problem though. One such library is GNU libffcall, another one is libffi. Both allow you to create closures that behave like normal C functions.

Upvotes: 3

Related Questions