C++/Arduino将函数作为参数传递

aus*_*nbv 8 c++ arduino c++11

我正在尝试创建一个回调样式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);
}
Run Code Online (Sandbox Code Playgroud)

编辑:

所以我想感谢大家到目前为止所有的答案,但是自从发布以来我学到了一些东西.Arduino的AVR不包括C++ <functional>.没有那个图书馆,这可能是一件事吗?

再次感谢你!

Mil*_*nek 4

非静态成员函数需要一个对象来工作,因此不能像普通函数指针一样传递和调用。

让你的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(); });
}
Run Code Online (Sandbox Code Playgroud)

通过一点额外的努力,您可以实现类似于 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);
}
Run Code Online (Sandbox Code Playgroud)

这并不能完全完成 C++17std::invoke所做的所有事情,但它足以实现基本回调。它还为您提供了额外的灵活性,您可以传递其他参数debounce,并且它们将传递给您的回调:

void foo(int num) { /*...*/ }

void loop() {
    Button b;
    b.debounce(foo, 42);
}
Run Code Online (Sandbox Code Playgroud)

如果您需要保存回调并稍后调用它,那么这实际上不起作用,但看起来这并不是您想要做的。