处理类内部的回调时,必须调用对非静态成员函数的引用

时间:2018-09-28 07:12:54

标签: c++ c c++11 pointers callback

我有以下由PiGPIO库提供的API代码。基本上,它允许您在引脚状态更改时设置回调函数。

通常,API如下所示:

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

它可以这样称呼:

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  
}

现在上面的代码可以正常工作了!


当我尝试将此代码包装在C ++类中时,出现了一个大问题。 该类应如下所示:

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;
    }
}

问题是不允许引用该函数,因为它不是静态函数。为了使事情更复杂,我不能仅仅使回调函数成为静态的。该回调函数将与其余的类方法及其内部变量进行很多交互。

您知道解决方法吗?还是使用指针之类的肮脏技巧?

2 个答案:

答案 0 :(得分:3)

不间断的C样式回调将接受void*参数以传递任意用户定义的数据。既然没有,那是库设计中的一个错误。向图书馆作者提出一个问题。

要变通解决此问题,您需要创建一个 closure 作为C兼容函数。标准C或C ++无法做到这一点。但是,有一些库(符合非标准规范,不能移植)可以解决此问题。一个这样的库是GNU libffcall,另一个是libffi。两者都允许您创建行为类似于普通C函数的闭包。

答案 1 :(得分:1)

gpio库代码没有Pin类的概念,但是在不知道要调用哪个Pin对象的情况下无法调用成员回调函数。因此,您需要一些(非成员)函数来找到要在函数调用中使用的Pin对象。

该信息似乎以int pin参数的形式出现。因此,您需要一个(免费)函数,该函数了解所有Pin对象以及它们具有的引脚号,找到正确的对象并调用其回调函数:

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);
}