【问题标题】:Anonymous inner class in C++ (Java-style listener)C++ 中的匿名内部类(Java 风格的侦听器)
【发布时间】:2019-04-12 23:48:07
【问题描述】:

我的 C/C++ 技能有点生疏,过去几年我大部分时间都在使用 Java。现在我刚开始玩 Arduino,并制作了一个简单的按钮类。我想添加一个事件监听器,所以我做了这样的事情:

class MyButton{
  public:
      MyButton(byte pin);
      bool isPressed();
      bool wasToggled();
      bool wasPressed();
      void eventLoop();
      inline void setListener(MyButtonListener* listener) { _listener = listener; }

  private:
      byte _pin;
      boolean _lastToggledState = false;
      MyButtonListener* _listener;
};



class MyButtonListener{
    public: 
        virtual void onPressed() = 0;

    private:
};

eventLoop() 方法(打算从 Arduino 的loop() 函数调用),调用监听器类中的onPressed() 方法:

void MyButton::eventLoop(){
    if( wasPressed() && _listener ){
        _listener->onPressed();
    }
}

到目前为止,一切都还好。但我无法弄清楚如何在主 Arduino 文件中实际分配和使用监听器。来自Java,我习惯于做类似的事情

myBtn.setListener( new MyButtonListener(){
    void onPressed(){
        Serial.println("Pressed");
        toggleLed(); // toggleLed() is a method in the main Arduino file
    }
});

我让它以一种非常复杂的方式工作,通过声明一个将toggleLed() 方法作为参数的新类(因为否则无法从新类中访问它):

class BtnListener : public MyButtonListener{
    public:
        BtnListener(void* toggleFunction) : _toggleFunction(toggleFunction){ };

    private:
        void (*_toggleFunction)();

        void onPressed(){
            Serial.println("Pressed");
            _toggleFunction();
        };
};



myBtn.setListener( new BtnListener(toggleLed) );

在 C++ 中肯定有更方便的方法来做这样的事情吗?一个监听器是可行的(但很难看)——我什至无法想象拥有 10 个按钮的恐怖,它们都需要不同的监听器实现......

【问题讨论】:

  • C++ 没有任何类似于内联生成的匿名类的东西,比如 Java。 C++ 不是 Java,它以完全不同的方式工作。在 C++ 中,使用内联 lambda 实现等效功能,并使用 std::function 进行类型擦除。 C++ 在过去十年左右发生了很大变化。如果不拿起 a good C++ book 并花一些时间阅读它,您将不会对现代 C++ 走得太远。
  • 以_开头的变量和函数为编译器保留
  • @Brandon 不是全部。
  • @Brandon _Uppercases 保留在任何地方,但 _lowercases 仅保留在全局命名空间中。这很好。

标签: c++ interface arduino listener anonymous-inner-class


【解决方案1】:

在您的情况下,一种或最简单的方法是将侦听器存储为 std::function<void()> 并且根本没有实际的类来为按钮侦听器建模(如果您真的想封装它,您仍然可以拥有它,但这不是必需的)。然后使用 lambda 函数调用 setListener,如下所示:

myBtn.setListener( [this]{
        Serial.println("Pressed");
        toggleLed(); // toggleLed() is a method in the main Arduino file
    });

【讨论】:

  • 你测试的是什么 Arduino?
【解决方案2】:

由于默认情况下 Arduino IDE 似乎不包含 <functional.h>,因此我无法使用 std::function<void()> 使用答案。然而,经过一些试验后,我意识到有一种更简单的方法,它还具有能够为听者建模的好处。

侦听器类仅包含指向每个侦听器回调函数的函数指针,以及为每个回调获取参数的构造函数。那么只需创建一个监听类的新实例并将每个回调作为 lambda 传递就非常方便了。

class MyButton{
    public:
        inline void setListener(MyButtonListener* listener) { _listener = listener; }

    private:
        MyButtonListener* _listener;          
 }


class MyButtonListener{
    public:
        MyButtonListener(void* onPressed, void* onToggled) : onPressed(onPressed), onToggled(onToggled) {};
        void (*onPressed)();
        void (*onToggled)();
};



void MyButton::eventLoop(){
    if( _listener ){
        if( wasPressed() ){
            _listener->onPressed();
        }

        if( wasToggled() ){
            _listener->onToggled();
        }        
    }
}



myBtn.setListener( 
    new MyButtonListener( 
        // onPressed
        [](){
            Serial.println("Pressed");
            toggleLed();
        },
        // onToggled
        [](){
            Serial.println("Toggled");
        }            
    )
);

不确定此解决方案是否有任何缺点,但它可以工作、可读且适合在 Arduino 上使用。

【讨论】:

  • 有趣,这可以编译吗?没有警告?你不得不求助于 void* 指针,这是(大)缺点但可以修复。由于 lamdba 到函数指针的转换,现在您可以拥有 UB。
猜你喜欢
  • 2014-05-11
  • 2014-09-20
  • 2011-03-18
  • 1970-01-01
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多