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