C++ 中的匿名内部 class(Java 式侦听器)

Anonymous inner class in C++ (Java-style listener)

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

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() 方法 class:

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
    }
});

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

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 个按钮都需要不同的听众实现的恐怖...

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

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

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

侦听器 class 仅包含指向每个侦听器回调函数的函数指针,以及一个为每个回调获取参数的构造函数。然后创建一个新的侦听器实例非常方便 class 并将每个回调作为 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 上使用。