将带有参数的回调函数传递给函数
Pass a callback function with a parameter to a function
我想调用以下函数并向它传递一个带参数的函数。这样做的目的是它应该使用我指定的参数调用该函数,以便我知道是什么触发了该函数(在这种情况下,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 ) );
}
虽然这给我以下错误:
error: lvalue required as unary ‘&’ operand
我无法真正理解我的理解,myValue 是左值还是不是?
我想做的事甚至可能吗?如果是这样怎么办?
wiringPiISR 函数来自一个名为 wiringPi 的库,我想尽可能避免修改它。
如果你有 c++11,我建议使用 std::function
- 它更干净。
如果不是,你的函数签名是错误的。你想要一个类型为 void(int)
的回调,但你的函数需要一个 void()
您需要使用 std::function
和 std::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
及更高版本。
如果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);
我查找了 wiringPiISR
并发现它是某种 api 调用,所以我假设您无法更改它。
话虽如此,但大多数 api 带有函数指针回调的调用看起来有点像这样
是有原因的
void setCallback( void (*function)(void* data), void* userdata);
这允许人们转换他们的 struct {blabla} data;
来添加一些用户数据,并且当函数被调用时,它被传递。
所以基本上,除了使用静态变量进行黑客攻击外,您不能传递任何参数。
你可以像这样结合 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));
无需补丁库,您可以使用所有 bind/function 优点。我将让您找到解决线程安全问题的方法...
它是如何工作的?简单地说,'std::bind' 将一个函数及其参数绑定到一个 std:function 对象中,然后可以 'called' 来自 myCallback 函数,该函数充当您传递的回调周围的垫片。我之前给回调函数起了一个令人困惑的名字,但这次编辑有望解决这个问题。
您可以"vomit"该功能。这不需要用户定义的可变全局变量并且是线程安全的,除非你有一个支持多线程但不支持每线程异常的编译器,这基本上是不可用的。
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
returns 后从未执行回调时有效。在这种情况下,您当然可以使用您想要的任何绑定状态进行回调。
我想调用以下函数并向它传递一个带参数的函数。这样做的目的是它应该使用我指定的参数调用该函数,以便我知道是什么触发了该函数(在这种情况下,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 ) );
}
虽然这给我以下错误:
error: lvalue required as unary ‘&’ operand
我无法真正理解我的理解,myValue 是左值还是不是?
我想做的事甚至可能吗?如果是这样怎么办? wiringPiISR 函数来自一个名为 wiringPi 的库,我想尽可能避免修改它。
如果你有 c++11,我建议使用 std::function
- 它更干净。
如果不是,你的函数签名是错误的。你想要一个类型为 void(int)
的回调,但你的函数需要一个 void()
您需要使用 std::function
和 std::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
及更高版本。
如果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);
我查找了 wiringPiISR
并发现它是某种 api 调用,所以我假设您无法更改它。
话虽如此,但大多数 api 带有函数指针回调的调用看起来有点像这样
是有原因的void setCallback( void (*function)(void* data), void* userdata);
这允许人们转换他们的 struct {blabla} data;
来添加一些用户数据,并且当函数被调用时,它被传递。
所以基本上,除了使用静态变量进行黑客攻击外,您不能传递任何参数。
你可以像这样结合 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));
无需补丁库,您可以使用所有 bind/function 优点。我将让您找到解决线程安全问题的方法...
它是如何工作的?简单地说,'std::bind' 将一个函数及其参数绑定到一个 std:function 对象中,然后可以 'called' 来自 myCallback 函数,该函数充当您传递的回调周围的垫片。我之前给回调函数起了一个令人困惑的名字,但这次编辑有望解决这个问题。
您可以"vomit"该功能。这不需要用户定义的可变全局变量并且是线程安全的,除非你有一个支持多线程但不支持每线程异常的编译器,这基本上是不可用的。
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
returns 后从未执行回调时有效。在这种情况下,您当然可以使用您想要的任何绑定状态进行回调。