I am trying to create a callback style API and am fairly new to C++. I keep getting the error error: invalid use of non-static member function
but am not sure the next steps. I would like to have the ability to pass an member function as an argument to another class.
The code is something like
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);
}
EDIT:
So I want to thank everyone for the awesome answers so far but I learned something since posting. Arduino's AVR does not include C++'s <functional>
. Is this a possible thing to do without that library?
Thank you again!
Non-static member functions require an object to work on, and thus can't be passed and called like normal function pointers.
The simplest way to make your debounce
method work, would be to use a lambda that captures your player
object and calls increment
on it:
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(); });
}
With a little bit of extra effort, you could implement something similar to C++17's std::invoke
to uniformly invoke any type of callable. Since you're on Arduino and don't have access to the C++ standard library, you'll need to implement std::remove_reference
and std::forward
yourself as well:
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);
}
This doesn't quite do everything that C++17's std::invoke
does, but it's close enough to implement a basic callback. It also gives you extra flexibility in that you could pass additional arguments to debounce
and they will be passed along to your callback:
void foo(int num) { /*...*/ }
void loop() {
Button b;
b.debounce(foo, 42);
}
This doesn't really work if you need to save the callback and call it later, but it doesn't look like that's what you're trying to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With