我正在尝试创建一个回调样式API,对于C ++来说还是一个新手。我一直收到错误error: invalid use of non-static member function
,但我不确定接下来的步骤。我希望能够将成员函数作为参数传递给另一个类。
代码类似于
class Button {
int buttonDownTime = 0;
int debounceTime = 2000;
...
template<typename Callback>
void debounce(Callback func) {
if (millis() - buttonDownTime > debounceTime) {
func();
}
}
}
class Player {
int playerCount = 0;
void incriment() {
playerCount++;
}
}
void loop() {
...
button.debounce(player.incriment);
}
编辑:
所以我想感谢所有人到目前为止给出了很棒的答案,但是自从发布以来我学会了一些东西。 Arduino的AVR不包括C ++的<functional>
。没有那个库,这可能是不可能的事情吗?
再次感谢你!
答案 0 :(得分:2)
使用std::bind
。成员函数调用的第一个(隐藏)参数是this
指针。使用std::bind
,您可以将成员函数包装到一个函数对象中,该对象具有this
指针&#34;烘焙在&#34;所以它不再需要它作为论据。
class Button {
int buttonDownTime = 0;
int debounceTime = 2000;
...
public:
template<typename Callback>
void debounce(Callback func) {
if (millis() - buttonDownTime > debounceTime) {
func();
}
}
}
class Player {
int playerCount = 0;
public:
void incriment() {
playerCount++;
}
}
void loop() {
...
Player player;
button.debounce( std::bind( &Player::incriment, &player ) );
}
答案 1 :(得分:2)
非静态成员函数需要一个对象才能处理,因此不能像普通函数指针那样传递和调用。
使debounce
方法有效的最简单方法是使用lambda捕获player
对象并在其上调用increment
:
class Button {
//...
template<typename Callback>
void debounce(Callback&& func) { // <<-- Note the && here, without
// it func would need to be
// copied
if (millis() - buttonDownTime > debounceTime) {
func();
}
}
}
void loop() {
//...
button.debounce([&player](){ player.incriment(); });
}
通过一些额外的努力,你可以实现类似于C ++ 17 std::invoke
的东西来统一调用任何类型的可调用。由于您使用的是Arduino并且无法访问C ++标准库,因此您还需要自己实现std::remove_reference
和std::forward
:
template <typename T>
struct remove_reference
{
using type = T;
};
template <typename T>
struct remove_reference<T&>
{
using type = T;
};
template <typename T>
struct remove_reference<T&&>
{
using type = T;
};
template <typename T>
constexpr T&& forward(typename remove_reference<T>::type& t)
{
return static_cast<T&&>(t);
}
template <typename T>
constexpr T&& forward(typename remove_reference<T>::type&& t)
{
return static_cast<T&&>(t);
}
template <typename Callable, typename... Args>
auto invoke(Callable&& func, Args&&... args)
-> decltype(forward<Callable>(func)(forward<Args>(args)...))
{
return forward<Callable>(func)(forward<Args>(args)...);
}
template <typename Callable, typename Class, typename... Args>
auto invoke(Callable&& method, Class&& obj, Args&&... args)
-> decltype((forward<Class>(obj).*method)(forward<Args>(args)...))
{
return (forward<Class>(obj).*method)(forward<Args>(args)...);
}
class Button {
//...
template<typename Callback, typename... Args>
void debounce(Callback&& func, Args&&... args) {
if (millis() - buttonDownTime > debounceTime) {
invoke(forward<Callback>(func),
forward<Args>(args)...);
}
}
}
void loop() {
//...
button.debounce(&Player::increment, player);
}
这并不能完成C ++ 17 std::invoke
所做的所有事情,但它足以实现基本的回调。它还为您提供了额外的灵活性,您可以将其他参数传递给debounce
,它们将被传递给您的回调:
void foo(int num) { /*...*/ }
void loop() {
Button b;
b.debounce(foo, 42);
}
如果您需要保存回调并稍后调用它,这不会真正起作用,但它看起来不像您正在尝试做的那样。
答案 2 :(得分:1)
在C ++中,类名(Player)将成为函数签名的一部分,语法变得非常重要。模板不能以优雅的方式解决它,甚至Boost给你的东西(看here)也不是很优雅(在我的书中)。
这是一个更简单的语法,可以实现相同的目标。
class Button {
int buttonDownTime = 0;
int debounceTime = 2000;
public:
template<typename CallbackClass>
void debounce(CallbackClass* po) {
if (millis() - buttonDownTime > debounceTime) {
po->increment();
}
}
};
class Player {
int playerCount = 0;
public:
void increment() {
playerCount++;
}
};
int main() {
Button button;
Player player;
button.debounce(&player);
}
答案 3 :(得分:1)
另一种有效的方法是使用纯虚拟func()方法定义一个接口,以便您可以将此接口的实现传递给debounce()方法并在其中调用其func()方法。
class YourInterface {
public:
virtual void func() = 0;
};
class Button {
public:
int buttonDownTime = 0;
int debounceTime = 2000;
...
void debounce(YourInterface yourInterface) {
if (millis() - buttonDownTime > debounceTime) {
yourInterface->func();
}
}
}
class Player {
public:
int playerCount = 0;
void increment() {
playerCount++;
}
}
void loop() {
...
button.debounce(player.increment);
}