C++内部匿名类(java风格侦听器)

C++内部匿名类(java风格侦听器),c++,interface,arduino,listener,anonymous-inner-class,C++,Interface,Arduino,Listener,Anonymous Inner Class,我的C/C++技能有些生疏,在过去的几年里,我主要是在Java中工作。现在我刚开始玩Arduino,并制作了一个简单的button类。我想添加一个事件侦听器,所以我做了如下操作: class MyButton{ public: MyButton(byte pin); bool isPressed(); bool wasToggled(); bool wasPressed(); void eventLoop(); inli

我的C/C++技能有些生疏,在过去的几年里,我主要是在Java中工作。现在我刚开始玩Arduino,并制作了一个简单的button类。我想添加一个事件侦听器,所以我做了如下操作:

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:
};
myBtn.setListener( [this]{
        Serial.println("Pressed");
        toggleLed(); // toggleLed() is a method in the main Arduino file
    });
拟从Arduino循环函数调用的eventLoop方法调用侦听器类中的onPressed方法:

void MyButton::eventLoop(){
    if( wasPressed() && _listener ){
        _listener->onPressed();
    }
}
到目前为止,一切顺利。但我不知道如何在主Arduino文件中实际分配和使用侦听器。来自爪哇,我习惯于做类似的事情

myBtn.setListener( new MyButtonListener(){
    void onPressed(){
        Serial.println("Pressed");
        toggleLed(); // toggleLed() is a method in the main Arduino file
    }
});
我以一种非常复杂的方式让它工作,通过声明一个新类,该类将toggled方法作为参数,因为它不能从新类中访问,否则:

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,并且根本没有一个实际的类来建模buttonlistener,如果您确实想封装它,您仍然可以使用它,但这不是必需的。然后使用lambda函数调用setListener,如下所示:

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:
};
myBtn.setListener( [this]{
        Serial.println("Pressed");
        toggleLed(); // toggleLed() is a method in the main Arduino file
    });

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

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:
};
myBtn.setListener( [this]{
        Serial.println("Pressed");
        toggleLed(); // toggleLed() is a method in the main Arduino file
    });

由于默认情况下Arduino IDE似乎不包含,因此我无法使用std::函数使用答案。然而,经过一些实验后,我意识到有一种更简单的方法,它也有能够为听者建模的好处

listener类只包含指向每个listener回调函数的函数指针,以及为每个回调获取参数的构造函数。然后,只需创建listener类的新实例并将每个回调作为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上使用。

由于默认情况下Arduino IDE似乎没有包含,因此我无法使用std::函数使用答案。然而,经过一些实验后,我意识到有一种更简单的方法,它也有能够为听者建模的好处

listener类只包含指向每个listener回调函数的函数指针,以及为每个回调获取参数的构造函数。然后,只需创建listener类的新实例并将每个回调作为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上使用。

C++没有类似于内联生成的匿名类,比如Java。C++不是java,它的工作原理是根本不同的。在C++中,使用内联lambDAS实现了等效功能,并且使用STD:函数实现类型擦除。C++在过去的十年里发生了很大的变化。如果不花一些时间,花一些时间阅读它,你将无法与现代C++非常接近。compiler@Brandon并非所有的@n.m.@Brandon\u大写字母都被保留在所有地方,但小写字母只保留在全局名称空间中。这是很好的。C++没有任何类似于内嵌生成的匿名类,比如java。C++不是java,它的工作原理是根本不同的。在C++中,使用内联lambDAS实现了等效功能,并且使用STD:函数实现类型擦除。C++在过去的十年里发生了很大的变化。如果不花一些时间,花一些时间阅读它,你将无法与现代C++非常接近。compiler@Brandon并非所有的@n.m.@Brandon\u大写字母都被保留在所有地方,但小写字母只保留在全局名称空间中。这很好。你在什么Arduino上测试它?在什么Arduino上测试它?很有趣,编译吗?没有警告?您必须求助于void*指针,这是一个很大的缺点,但可以修复。现在,由于lamdba到函数指针的转换,您可以使用UB。有趣的是,这会编译吗?没有警告?您必须求助于void*指针,这是一个很大的缺点,但可以修复。现在,由于lamdba到函数指针的转换,您可以使用UB。