интерфейс — Анонимный внутренний класс в C ++ (слушатель в стиле Java)

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

0

Решение

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

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

Другие решения

Поскольку 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.

2

По вопросам рекламы [email protected]