C++内部匿名类(java风格侦听器)
我的C/C++技能有些生疏,在过去的几年里,我主要是在Java中工作。现在我刚开始玩Arduino,并制作了一个简单的button类。我想添加一个事件侦听器,所以我做了如下操作: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
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。