我正在尝试实现一个回调,它将控制权从中断服务例程传递到 C++ 类的成员函数。我认为 lambdas 和闭包是执行此操作的便捷方式,但我在实现时遇到了麻烦。下面是我的代码的简化版本。
我遇到的问题是如何将“函数指针”存储到“lambda”。
class Gpio
{
public:
typedef void (*ExtiHandler)();
private:
ExtiHandler handler;
public:
void enable_irq(ExtiHandler handler_in)
{
// enable interrupt
// ...
// save handler so callback can be issued later
handler = handler_in;
}
};
class Button
{
private:
Gpio& pin;
public:
Button(Gpio& pin_in) : pin(pin_in)
{
};
void button_pressed()
{
// do something
}
void init()
{
pin.enable_irq([this]() { this->button_pressed(); });
}
};
编译失败,出现以下错误信息;
no matching function for call to 'Gpio::enable_irq(Button::init()::<lambda()>)'candidate: void Gpio::enable_irq(Gpio::ExtiHandler) no known conversion for argument 1 from 'Button::init()::<lambda()>' to 'Gpio::ExtiHandler {aka void (*)()}' Build failed
如何修改此代码以解决编译错误?
最佳答案
问题是,enable_irq
函数需要类型为 void (*ExtiHandler)()
的类型化函数指针 不是lambda 函数。
也就是说,这里
pin.enable_irq([this]() { this->button_pressed(); });
您正在尝试将 lambda 函数(通过捕获实例)存储到 typed function pointer .如果 lambda 是无捕获的 lambda,您可以(轻松地)将 lambda 转换为函数指针。
参见 [expr.prim.lambda.closure] (第 7 节)
The closure type for a non-generic lambda-expression with no lambda-capture whose constraints (if any) are satisfied has a conversion function to pointer to function with C++ language linkage having the same parameter and return types as the closure type's function call operator.
自 lambdas are not just ordinary functions捕获它需要保持状态, 您找不到任何简单或常规的解决方案来使它们分配给函数指针。
解决方案 - 1
最简单的解决方案是使用 std::function
相反,通过支付一些 type erasure overhead .这意味着,在您的代码中,只需更改
typedef void(*ExtiHandler)();
到
typedef std::function<void()> ExtiHandler;
// or
// using ExtiHandler = std::function<void()>;
解决方案 - 2
Can this be accomplished without using the STL?
是。在对该主题进行了一些研究之后,我想出了一个类型特征解决方案来存储 lambdas with closure到等效的类型化函数指针。
#include <iostream>
template<typename Lambda> struct convert_lambda : convert_lambda<decltype(&Lambda::operator())> {};
template<typename Lambda, typename ReType, typename... Args>
struct convert_lambda<ReType(Lambda::*)(Args...) const>
{
using funPtr = ReType(*)(Args...);
static funPtr make_function_ptr(const Lambda& t)
{
static const Lambda& lmda = t;
return [](Args... args) { return lmda(args...); };
}
};
template<typename Lambda> using convert_lambda_t = typename convert_lambda<Lambda>::funPtr;
template<typename Lambda> constexpr convert_lambda_t<Lambda> make_function_ptr(const Lambda& t)
{
return convert_lambda<Lambda>::make_function_ptr(t);
}
用法: SEE LIVE EXAMPLE
您现在可以简单地继续您的
Gpio
和Button
类,而无需 改变任何东西。:pin.enable_irq(make_function_ptr([this]() { this->button_pressed(); })); // or // pin.enable_irq(make_function_ptr([&]() { this->button_pressed();}));
或带参数。例如
int aa = 4; auto lmda = [&aa](const int a, const float f) { std::cout << a * aa * f << std::endl; }; void(*fPtrTest)(const int, const float) = make_function_ptr(lmda); fPtrTest(1, 2.0f);
缺点:解决方案 - 2:
不能够识别说明符的可选序列。(即,
mutable
、constexpr
)不能够将参数包转发给特征。 IE, 以下是不可能的:
return [](Args&&... args) { return lmda(std::forward<Args>(args)...); };
关于c++ - 使用带闭包的 lambda 回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52462864/