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

Posted

技术标签:

【中文标题】在类内部处理回调时必须调用对非静态成员函数的引用【英文标题】:Reference to non-static member function must be called when processing callbacks inside a class 【发布时间】:2018-09-28 07:12:53 【问题描述】:

我有以下由 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;
    

问题是不允许对该函数的引用,因为它不是静态函数。对于更复杂的事情,我不能只将回调函数设为静态。该回调函数将与其他类方法及其内部变量进行大量交互。

您知道解决方法吗?或者可能是使用指针或其他东西的肮脏黑客?

【问题讨论】:

通常,回调提供一个客户端数据指针。使用客户端数据指针,您可以使回调方法成为静态函数并将对象指针作为客户端数据传递。因此,静态方法/回调函数可以使用对象指针/客户端数据访问对象成员。没有客户数据,机会非常糟糕。唯一的机会,将对象指针存储在全局变量中,该变量可用于(静态)回调方法。 回调函数的每个int pin 值是否恰好映射到一个Pin 对象? 在问题中提供所有相关信息很重要。这个问题缺少一条基本信息,即int gpioSetAlertFuncEx(unsigned user_gpio, gpio_callback_t f, void *userdata) 的存在。有了这条信息,所有现有的答案和 cmets 都变得无关紧要了。 @MaxLanghof 我不确定。 @n.m.那时我不知道那条信息是相关的。但在这种情况下,为了让其他人的问题更具信息性,我想我们可以假设扩展功能 api 不可用。 【参考方案1】:

完整的 C 风格回调将接受 void* 参数来传递任意用户定义的数据。由于这个没有,它是库设计中的一个错误。向图书馆作者提出问题。

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

【讨论】:

好的,那个库的问题是它还有一个扩展版本的函数,允许你传递一些用户数据。函数是int gpioSetAlertFuncEx(unsigned user_gpio, gpio_callback_t f, void *userdata)。但我仍然看不到连接。这对我有什么帮助? @caffeine 请提出一个单独的问题,或者搜索这个网站。有很多例子。在紧要关头,您创建一个静态成员函数适配器,将其注册为回调,然后从那里调用您的非静态成员。将void* 参数强制转换为Pin* @caffeine 我知道的最好的方法是使用reinterpret_cast from/to void*/Pin*【参考方案2】:

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

【讨论】:

以上是关于在类内部处理回调时必须调用对非静态成员函数的引用的主要内容,如果未能解决你的问题,请参考以下文章

MFC GetEditCtrl 报错非静态成员函数的非法调用

错误分析:非静态成员引用必须与特定对象相对

VS提示“非静态的字段,方法或属性要求对象引用”

Java内部类

C++面向对象-static、const

QtConcurrent 错误:对非静态成员的引用