2017-06-19 165 views
4

我想創建一個回調風格的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

回答

2

使用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

1

非靜態成員函數需要一個對象來處理,因此不能像普通函數指針那樣傳遞和調用。

,讓您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); 
} 

此,如果您需要保存回調,過一會兒給它並沒有真正的工作,但它看起來並不像你想要做的那樣。

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

這是偉大的,但限制你只能夠增加一個球員debounce, – austinbv

+0

@austinbv,沒有這個將允許您增加具有增量方法的任何對象堆積器將爲傳遞給它的每種類型生成一個單獨的去抖動方法。 – diverscuba23

1

另一種有效的方法是定義與純虛擬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); 
}