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

时间:2017-06-19 20:00:30

标签: c++ c++11 arduino

我正在尝试创建一个回调样式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>。没有那个库,这可能是不可能的事情吗?

再次感谢你!

4 个答案:

答案 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 ) );
}

Simplified demo at Coliru

答案 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_referencestd::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);
}