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

O'Neil
  • 3,790
  • 4
  • 16
  • 30
  • Array of `std::function` and filling with lambdas doesn't work? – huseyin tugrul buyukisik Sep 13 '19 at 15:07
  • Pointers to member functions and pointers to static/free function are different things. Which ones are you trying to use? – Yksisarvinen Sep 13 '19 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. – Max Langhof Sep 13 '19 at 15:08
  • I can change them to static, there only needs to be one instance. – unprotested Sep 13 '19 at 15:08
  • @MaxLanghof How would I change them to free functions? – unprotested Sep 13 '19 at 15:09
  • @unprotested By moving them out of the class they are in. It appears that you are trying to write Java/C# code in C++, which is not going to get you very far. You should obtain a [good book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) and start from the beginning, because you can't really learn C++ by trial and error. – Max Langhof Sep 13 '19 at 15:10
  • 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++. – PaulMcKenzie Sep 13 '19 at 15:10
  • @MaxLanghof I know very little about C++ lol – unprotested Sep 13 '19 at 15:10
  • @unprotested I accidentally hit enter and posted my comment too soon. Please see the edit. – Max Langhof Sep 13 '19 at 15:11
  • @MaxLanghof Noted. Although so far with arduino I've coded entirely with my knowledge from C# and thought it was C# for a long time. I recently learned when I tried doing this that is was more similar to C++. – unprotested Sep 13 '19 at 15:14
  • @huseyintugrulbuyukisik arduino does't have the C++ standard library – bolov Sep 13 '19 at 15:14
  • 1
    please create a [mcve]. It looks like those are class methods and not free functions. – bolov Sep 13 '19 at 15:15
  • @bolov I have edited my original post. – unprotested Sep 13 '19 at 15:21
  • If you make the functions `static`, it will work. – alain Sep 13 '19 at 15:29

1 Answers1

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]();
    }  
};
bolov
  • 72,283
  • 15
  • 145
  • 224
  • +1 for the `using`. That’s a too often neglected requirement for keeping your sanity when dealing with function pointers, especially *member* function pointers. – besc Sep 13 '19 at 15:47