kni*_*ick 5 c++ lambda closures function-pointers c++11
我正在尝试实现一个回调,该回调将控制从中断服务例程传递到 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(); });
}
};
Run Code Online (Sandbox Code Playgroud)
编译失败并显示以下错误消息;
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
Run Code Online (Sandbox Code Playgroud)
如何修改此代码以解决编译错误?
问题是,该enable_irq
函数需要一个类型化的函数指针,void (*ExtiHandler)()
而不是 lambda函数。
也就是说,这里
pin.enable_irq([this]() { this->button_pressed(); });
Run Code Online (Sandbox Code Playgroud)
您正在尝试将 lambda 函数(捕获实例)存储到类型化函数指针。如果它是一个无捕获的 lambda,您可以将 lambda 转换为函数指针(很容易)。
参见[expr.prim.lambda.closure] (第 7 节)
没有满足约束(如果有)的非泛型 lambda 表达式的闭包类型具有一个转换函数,指向具有 C++ 语言链接的函数的指针,该函数具有与闭包类型的函数调用运算符相同的参数和返回类型.
由于lambda 不只是普通函数并且捕获它需要保留状态,因此您找不到任何简单或常规的解决方案来使它们分配给函数指针。
最简单的解决方案是std::function
通过支付某种类型擦除开销来代替使用。这意味着,在您的代码中,只需要更改
typedef void(*ExtiHandler)();
Run Code Online (Sandbox Code Playgroud)
到
typedef std::function<void()> ExtiHandler;
// or
// using ExtiHandler = std::function<void()>;
Run Code Online (Sandbox Code Playgroud)
这可以在不使用 STL 的情况下完成吗?
是的。在对这个主题进行了一些小的研究之后,我想出了一个类型特征解决方案来存储带有闭包的lambdas到等效的类型化函数指针。
#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);
}
Run Code Online (Sandbox Code Playgroud)
用法: 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();}));
Run Code Online (Sandbox Code Playgroud)或有论据。例如
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);
Run Code Online (Sandbox Code Playgroud)缺点:解决方案 - 2:
是不能够识别的说明符的可选序列(即mutable
,constexpr
)
是不是能够转发参数包的性状。即,以下情况是不可能的:
return [](Args&&... args) { return lmda(std::forward<Args>(args)...); };
Run Code Online (Sandbox Code Playgroud)仅当 lambda 捕获列表为空时,才可以将闭包对象分配给函数指针,在您的情况下,不满足此条件 - [this]
。
您可以用作std::function
包装器来存储您的闭包:
#include <functional>
class Gpio
{
public:
using ExtiHandler = std::function<void()>;
private:
std::function<void()> handler;
public:
void enable_irq(const ExtiHandler& handler_in)
{
handler = handler_in;
}
};
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1714 次 |
最近记录: |