1

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 (*)()'

14
  • Array of std::function<void(void)> and filling with lambdas doesn't work? Commented Sep 13, 2019 at 15:07
  • Pointers to member functions and pointers to static/free function are different things. Which ones are you trying to use? Commented Sep 13, 2019 at 15:08
  • In C++ there is a difference between member functions and free functions. Your list can only store free functions, but you are trying to put member functions into it. Commented Sep 13, 2019 at 15:08
  • 4
    I've tried doing what I would normally do in C# / Java but that didn't work -- Advice -- Never do this. Do not use other languages to figure out how to do something in C++. Commented Sep 13, 2019 at 15:10
  • 1
    please create a minimal reproducible example. It looks like those are class methods and not free functions. Commented Sep 13, 2019 at 15:15

1 Answer 1

4

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]();
    }  
};
Sign up to request clarification or add additional context in comments.

1 Comment

+1 for the using. That’s a too often neglected requirement for keeping your sanity when dealing with function pointers, especially member function pointers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.