调整 lambda 语法以匹配所需的签名

Adjust lambda syntax to match required signature

提问人:Mark 提问时间:11/14/2023 最后编辑:Mark 更新时间:11/16/2023 访问量:47

问:

我找到了这个问题,但答案似乎不符合我的要求。 答案建议使用函数,而是我需要访问类成员。static

我(不起作用的)理想代码是:

在 myclass.h 中:

private:
    int foo = 0;

    hw_timer_t *_timer1s;
    void Setup();

在 myclass.cpp 中:

void MyClass::Setup()
{
    _timer1s = timerBegin(0, 80, true);
    timerAttachInterrupt(_timer1s, [this]()
    {
        foo++;
    }, true);
}

当然,我得到了已知的转换错误:

不存在从“lambda ->void”到“void (*)()”的合适转换函数

我理解了链接问题的解释,但我不明白如何更改建议的代码以捕获/访问类成员(即)。使用函数不允许我这样做。foostatic

这里的正确语法是什么?

更新

根据要求,我添加了整个类代码:

我的class.h

#ifndef MYCLASS_H
#define MYCLASS_H

#include <Arduino.h>
#include <RTClib.h>

class MyClass
{
public:
    MyClass();

private:
    int foo = 0;

    hw_timer_t *_timer1s;
    void Setup();
};

#endif // MYCLASS_H

我的类.cpp:

#include "myclass.h"

MyClass::MyClass()
{
    Setup();
}

void MyClass::Setup()
{
    _timer1s = timerBegin(0, 80, true);
    timerAttachInterrupt(_timer1s, [this]()
    {
        foo++;
    }, true);
    timerAlarmWrite(_timer1s, 1000000, true);
    timerAlarmEnable(_timer1s);
}

以下是编译输出错误:

src/myclass.cpp: In member function 'void MyClass::Setup()':
src/myclass.cpp:40:12: error: cannot convert 'MyClass::Setup()::<lambda(void*)>' to 'void (*)()'
     }, true);

In file included from /home/mark/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal.h:92,
                 from /home/mark/.platformio/packages/framework-arduinoespressif32/cores/esp32/Arduino.h:36,
                 from include/myclass.h:4,
                 from src/myclass.cpp:1:
/home/mark/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-timer.h:40:53: note:   initializing argument 2 of 'void timerAttachInterrupt(hw_timer_t*, void (*)(), bool)'
 void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge);

而 Visual Studio Code 的内联助手报告:

不存在从“lambda ->void”到“void (*)()”的合适转换函数

Lambda 静态 ESP32 Arduino-C++

评论

0赞 463035818_is_not_an_ai 11/14/2023
它与语法无关,但 A 是一个裸函数指针,它不携带任何数据或状态,您可以在其中捕获某些内容void(*)()
1赞 Mark 11/14/2023
在投反对票或结束问题之前,你们能告诉我我需要添加哪些调试细节吗?
1赞 Mark 11/14/2023
因此,答案@463035818_is_not_an_ai“您无法从该中断处理程序访问任何成员对象”?即使我不使用 lambda 而是使用显式 ISR 函数?
0赞 463035818_is_not_an_ai 11/14/2023
请阅读最小可重现示例并尝试提供一个示例。您只发布了片段,然后说“当然我得到了已知的转换错误:”,但事实并非如此。当您尝试编译发布的代码时,会有很多其他错误,但没有关于从 lambda 到void(*)()
1赞 Mark 11/14/2023
@463035818_is_not_an_ai我会更新问题,但我向你保证,这是我得到的唯一错误。

答:

1赞 Ted Lyngmo 11/15/2023 #1

捕获 lambda 将与您需要提供给 的 lambda 不匹配。解决方案是改用 timerAttachInterruptArg 并作为参数提供。void(*)()timerAttachInterruptthis

void timerAttachInterruptArg(hw_timer_t * timer, void (*userFunc)(void*), void * arg);

  • timer- 计时器结构。
  • userFunc- 触发中断时调用的功能。
  • arg- 指向中断参数的指针。
void MyClass::Setup()
{
    _timer1s = timerBegin(0, 80, true);
    timerAttachInterruptArg(_timer1s, [](void* instance)
    {
        // cast `instance` back to a `MyClass*`:
        auto This = static_cast<MyClass*>(instance);
        This->foo++;
    }, this);
//     ^^^^   this becomes `void* instance` in the lambda

    //...
}

当然,如果是私有的,这是行不通的。您可以调用公共成员函数来解决此问题。foo

例:

class MyClass {
public:

    void timer_event() {
        // called by the lambda on the correct instance
        foo++;
    }

    void Setup() {
        _timer1s = timerBegin(0, 80, true);
        timerAttachInterruptArg(_timer1s, [](void* instance) {
            static_cast<MyClass*>(instance)->timer_event();
        }, this);

        // ...
    }

};

评论

0赞 Mark 11/16/2023
谢谢你的提示。我正在挖掘为什么它找不到.我在 ESP32 平台上,它应该在那里。timerAttachInterruptArg
0赞 Ted Lyngmo 11/16/2023
@Mark 不客气!我用我阅读的文档的链接更新了答案。
0赞 Ted Lyngmo 11/16/2023
@Mark 如果你没有它,也许你有一个旧版本的库?cores/esp32/esp32-hal-timer.h 似乎支持我的建议 - 但它不支持边缘触发。timerAttachInterruptbool
1赞 Ted Lyngmo 11/16/2023
@Mark 我的建议仅在新的 3.0 API github.com/espressif/arduino-esp32/releases/tag/3.0.0-alpha2 中可用 - 它处于 alpha 状态,但如果您想尝试一下,您需要安装 3.0.0-alpha2 版本(存档列在链接中)。