Анонимный внутренний класс в C ++ (слушатель в стиле Java) - PullRequest
0 голосов
/ 09 ноября 2018

Мои навыки 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 кнопок, которые все требуют разных реализаций слушателя ...

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Поскольку в Arduino IDE по умолчанию не входит <functional.h>, я не смог использовать ответ, используя std::function<void()>. Однако после некоторых экспериментов я понял, что есть более простой способ, который также имеет преимущество в том, что он способен моделировать слушателя.

Класс слушателя просто содержит указатели на функции для каждой функции обратного вызова слушателя и конструктор, который принимает аргумент для каждого обратного вызова. Тогда очень удобно просто создать новый экземпляр класса слушателя и передать каждый обратный вызов как лямбду.

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.

0 голосов
/ 09 ноября 2018

В вашем случае, один или самый простой метод будет хранить слушателя как std::function<void()> и не иметь фактического класса для моделирования ButtonListener вообще (у вас все еще может быть это, если вы действительно хотите инкапсулировать это , но это не обязательно). Затем используйте лямбда-функцию для вызова setListener, примерно так:

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