将带参数的回调函数传递给函数

时间:2015-06-12 21:19:37

标签: c++ c++11 function-pointers void-pointers

我想调用以下函数并将其传递给带参数的函数。这样做的目的是它应该用我指定的参数调用函数,所以我知道是什么触发了函数(在这种情况下是Raspberry Pi上的gpio引脚)。

int wiringPiISR( int pin, int edgeType, void (*function)( void ) );

目前我有:

for ( int i = 0; i < myValues.size(); ++i )
{
    int myValue = myValues[ i ];
    wiringPiISR( myValue, INT_EDGE_RISING, &myCallback( myValue ) );
}

虽然这给了我以下错误:

  

错误:左值作为一元'&amp;'操作数

我无法理解我的理解,myValue是左值还是不是?

这是我想做的甚至可能吗?如果是这样的话? 函数wiringPiISR来自一个名为wiringPi的库,我想尽量避免修改它。

6 个答案:

答案 0 :(得分:3)

你可以将imreal和Ryan Haining的答案结合起来。

std::function<void()> cbfunc;

void myCallback()
{
  cbfunc();
}
void myWiringPiISR(int val, int mask, std::function<void()> callback)
{
  cbfunc = callback;
  wiringPiISR(val, mask, &myCallback);
}

...然后使用它......

void myActualCallback(int v)
{
  ... do something...
}

myWiringPiISR(myValue, INT_EDGE_RISING, std::bind(myActualCallback, myValue));

无需修补库,您可以使用所有绑定/功能的优点。我会让你找到解决线程安全问题的方法......

它是如何工作的?简单地说'std :: bind'将一个函数和它的参数绑定到一个std:function对象中,然后可以从myCallback函数“调用”该函数,该函数充当你传递的回调的垫片。之前我给回调函数一个令人困惑的名字,但是这个编辑有希望修复它。

答案 1 :(得分:3)

你可以“呕吐”这个功能。这不需要用户定义的可变全局变量,并且是线程安全的,除非你有一个支持多线程的编译器,但不支持基本上不可用的每线程异常。

myWiringPiISRWrapper(Value value, int edge, std::function<void()> func) {
    try {
        throw &func;
    } catch(...) {
        myWiringPiISR(value, edge, [] {
            try {
                throw;
            } catch(std::function<void()>* func) { 
                (*func)();
            }
        });
    }
}

令人作呕和缓慢,但它完全被封装,我觉得这是值得的好处。请注意,这仅在调用myWiringPiISR后才返回回调时才会起作用。在这种情况下,您当然可以根据需要进行回调。

答案 2 :(得分:2)

如果myValue是您可以在编译时决定的,您可以静态设置它并使用中间函数传入。

void myCallbackHelper() {
    static constexpr int myValue = 3;
    myCallback(myValue);
}

wiringPiISR(myValue, INT_EDGE_RISING, &myCallbackHelper);

如果您需要在运行时确定myValue,您仍然可以完成此操作,但不是真正的线程安全。

int& getMyValue() {
    static int myValue;
    return myValue;
}

void setMyValue(int i) {
    getMyValue() = i;
}

void myCallbackHelper() {
    myCallback(getMyValue());
}

然后设置并调用

setMyValue(3);
wiringPiISR(myValue, INT_EDGE_RISING, &myCallbackHelper);

答案 3 :(得分:2)

我查了wiringPiISR并发现它是某种api电话,所以我假设你不能改变它。

话虽如此,有一个原因,大多数带有函数指针回调的api调用看起来都像这样

void setCallback( void (*function)(void* data), void* userdata);

这允许人们投射他们的struct {blabla} data;来添加一些用户数据,并且当调用该函数时,它会被传递。

所以基本上,除了使用静态变量进行黑客攻击之外,你无法传递任何参数。

答案 4 :(得分:1)

您需要使用std::functionstd::bind

将您的功能签名更改为

int wiringPiISR (int pin, int edgeType,  std::function<void()> func);

您可以使用func()

在内部调用回调

您致电:

int myValue = 3;
wiringPiISR(myValue, INT_EDGE_RISING, std::bind(myCallback, myValue));

这样做是为了创建一个std::function对象(即一个可调用对象)来包装你的函数并将你想要的值保持在它的状态。

这仅适用于C++11及更新版本。

答案 5 :(得分:0)

如果你有c ++ 11,我建议使用std::function - 它更清洁。

如果没有,您的功能签名是错误的。您想要一个类型为void(int)的回调,但您的函数需要void()