C++/Arduino传递一个函数作为参数

问题描述:

我想创建一个回调风格的API,而且对于C++来说是相当新的。我不断收到错误error: invalid use of non-static member function,但我不确定接下来的步骤。我希望能够将成员函数作为参数传递给另一个类。C++/Arduino传递一个函数作为参数

的代码是一样的东西

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>。如果没有这个图书馆,这是否可能?

再次感谢您!

+2

你想['std :: function'](http://en.cppreference.com/w/cpp/utility/functional/function)。 –

+2

我们确实需要一个规范的。谁想制作一个? – Brian

+0

@CaptainObvlious saddly功能不包括在AVR中为arduino :( – austinbv

使用std::bind。成员函数调用的第一个(隐藏)参数是指针this。使用std::bind,可以将成员函数包装到一个函数对象中,该函数对象的指针“已烘焙”,因此不再需要它作为参数。

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

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

,让您debounce方法工作的最简单方法,是使用捕捉你的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); 
} 

此,如果您需要保存回调,过一会儿给它并没有真正的工作,但它看起来并不像你想要做的那样。

在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); 
} 
+0

这是伟大的,但限制你只能够增加一个球员debounce, – austinbv

+0

@austinbv,没有这个将允许您增加具有增量方法的任何对象堆积器将为传递给它的每种类型生成一个单独的去抖动方法。 – diverscuba23

另一种有效的方法是定义与纯虚拟FUNC()方法的接口,这样就可以通过这个接口的实现你的防抖动()方法,并没有调用它的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); 
}