Reputation: 31
I am doing a small project using arduino and I need to use an array of methods.
I've tried doing what I would normally do in C# / Java but that didn't work. I went online and tried many different examples and I kept getting lead to the same error message.
class ColourSensor{
public:
void (*routine[3])() = {
routine0,
routine1,
routine2
};
void routine0();
void routine1();
void routine2();
};
When I compile I get the following error:
cannot convert 'ColourSensor::routine0' from type 'void (ColourSensor::)()' to type 'void (*)()'
Upvotes: 1
Views: 216
Reputation: 75854
Things get complicated because they are methods. A method has an implicit hidden this
parameter, so it's a different type than a free functions.
This is the correct syntax:
class ColourSensor
{
public:
using Routine = void (ColourSensor::*)();
Routine routines[3] = {
&ColourSensor::routine0,
&ColourSensor::routine1,
&ColourSensor::routine2,
};
void routine0();
void routine1();
void routine2();
void test()
{
// calling syntax:
(this->*routines[0])();
}
};
An alternative which simplifies the calling syntax using non-capturing lambdas (which can decay to function pointer):
class ColourSensor
{
public:
using Routine = void (*)(ColourSensor*);
Routine routines[3] = {
[](ColourSensor* cs) { return cs->routine0(); },
[](ColourSensor* cs) { return cs->routine1(); },
[](ColourSensor* cs) { return cs->routine2(); }
};
void routine0();
void routine1();
void routine2();
void test()
{
// simpler calling syntax
routines[0](this);
}
};
Going one step further (and off the rails), if you know you always use this
object to call the methods you need to capture this
in lambda. The problem now is that capturing lambdas can't decay to function pointers and since every lambda is a different type you can't store them in an array. The usual solution in C++ is type erasure with std::function
. Unfortunately arduino C++ environment doesn't have the C++ standard library (because of the size constraints). For reference, this is how it would have looked (and since we are using the standard library, we use std::array
):
/// !! not working in Arduino !!
#include <functional>
#include <array>
class ColourSensor
{
public:
std::array<std::function<void(void)>, 3> routines = {
[this]() { return this->routine0(); },
[this]() { return this->routine1(); },
[this]() { return this->routine2(); }
};
void routine0();
void routine1();
void routine2();
void test()
{
// simple calling syntax
routines[0]();
}
};
There is a workaround for this. And although it's a bit of work to setup, it's actually faster than the std::function
because we don't use type erasure:
class ColourSensor;
class Routine
{
private:
using R = void (ColourSensor::*)();
R routine_;
ColourSensor* calling_obj_;
public:
Routine(R routine, ColourSensor* calling_obj)
: routine_{routine}, calling_obj_{calling_obj}
{}
void operator()();
};
class ColourSensor
{
public:
Routine routines[3] = {
Routine{&ColourSensor::routine0, this},
Routine{&ColourSensor::routine1, this},
Routine{&ColourSensor::routine2, this}
};
void routine0();
void routine1();
void routine2();
void test()
{
// simple calling syntax
routines[0]();
}
};
void Routine::operator()() { return (calling_obj_->*routine_)(); }
If however your routines don't use the state of the ColourSensor
, than you can make them static functions:
class ColourSensor
{
public:
using Routine = void (*)();
Routine routines[3] = {
routine0,
routine1,
routine2,
};
static void routine0();
static void routine1();
static void routine2();
void test()
{
routines[0]();
}
};
Upvotes: 4