1

I am new to C++ & I need to call functions of one class from another class. And so that the classes do not know anything about each other. How to do it in Arduino environment?

class Encoder {
  using CallBack2 = void( * )(const int, const int);
  using CallBack0 = void( * )();
  private:
    CallBack2 callbackUpDown;
    CallBack0 callbackTab;
    CallBack0 callbackLongPress;
  public:
    Encoder() {}

  void Setup(CallBack2 updown, CallBack0 tab, CallBack0 cbLongPress) {
    callbackUpDown = updown;
    callbackTab = tab;
    callbackLongPress = cbLongPress;
  }

  void Loop() {
    if (true) {
      callbackUpDown(-1, 300);
      callbackTab();
      callbackLongPress();
    }
  }
};

class Menu {
  public:
    void UpDown(const int direction,
      const int count) {}
      void Tab() {}
};

class RTC {
  public:
    void Setup() {}
    void Toggle() {}
};

class Display {
  public:
    void Toggle() {}
};

Encoder encoder = Encoder();
RTC rtc = RTC();
Menu menu = Menu();
Display display = Display();

void setup() {
  rtc.Setup();
  encoder.Setup(menu.UpDown, menu.Tab, []{display.Toggle();rtc.Toggle();});
}

void loop() {
  encoder.Loop();
}

Output:

In file included from sun.ino:47:0:
callback.h: In function 'void setup()':
callback.h:52:75: error: invalid use of non-static member function 'void Menu::UpDown(int, int)'
   encoder.Setup(menu.UpDown, menu.Tab, []{display.Toggle(); rtc.Toggle();});
                                                                           ^
callback.h:28:7: note: declared here
  void UpDown(const int direction, const int count) {}
       ^~~~~~

Thank you!

3
  • 2
    Classes are designed to be self contained. If you really need to use a function in a different class consider refactoring your code so as to avoid this. Alternatively you could put the function in question into a new class which both existing classes inherit from. Thereby giving access to that function from both existing classes.
    – st2000
    Oct 19, 2022 at 14:52
  • pass an object of that class. arduino.stackexchange.com/questions/90329/…
    – Juraj
    Oct 19, 2022 at 15:06
  • Are there no interfaces in C++ to Inject Dependencies, Dependency Inversion?
    – Andre
    Oct 20, 2022 at 9:29

2 Answers 2

1

What you are doing here

encoder.Setup(menu.UpDown, menu.Tab, []{display.Toggle();rtc.Toggle();});

is not only passing a function pointer. You are passing a member function pointer. This is 2 levels more difficult than function pointers on their own.

  1. The syntax of declaring a member function pointer is different. You need to include the class name in the function pointer declaration. It would look like

    using CallBack2 = void( Menu::* )(const int, const int);
    
  2. When calling a member function pointer, you need an object to call the function on.

Unfortunately, this is so hard that I can't turn your code into a working example. If you want to be able to read your code in 3 months or if you want others to be able to read your code, stick to normal function pointers. They are hard enough.

Suggestion: implement free functions that do what you want:

void menuUpDown(const int dir, const int count)
{
  menu.UpDown(dir, count);
}
void menuTab()
{
  menu.Tab();
}

void setup() {
  rtc.Setup();
  encoder.Setup(menuUpDown, menuTab, []{display.Toggle();rtc.Toggle();});
}

Or, since you're already using lambdas (are you aware of that?), you can also

void setup() {
  rtc.Setup();
  encoder.Setup(
    [](const int dir, const int count){menu.UpDown(dir, count);}, 
    []{menu.Tab();},
    []{display.Toggle();rtc.Toggle();}
  );
}

Note that pupils and students might not have been introduced to the concept of lambdas. If you publish such code online, people might not be able to change it themselves because they don't understand it.

6
  • Nice answer! Thank you 👍
    – Andre
    Oct 19, 2022 at 17:21
  • @Andre: you're welcome. Once you have tried the suggestion and tested that it actually works for you, you may mark this question as solved by checking the checkmark beneath an answer so that others know they don't need to spend time on this any more. No need to hurry, take your time to check that it's really the answer you need. Oct 19, 2022 at 17:24
  • 1
    @Andre: your design looks more like a C# or Java design. Be careful with that on Arduino. You may run out of memory if your designs are very sophisticated and your code is too PC-like. Oct 19, 2022 at 17:29
  • 1
    @Andre: regarding OOM, I don't have much advise. Mainly avoid new (which I see you don't use, that's great). Next, avoid copies. Encoder encoder = Encoder(); could just be Encoder encoder;. (Yes, we have guaranteed copy elision in modern C++, but nobody really knows what C++ version Android actually supports) Oct 21, 2022 at 15:04
  • 1
    @Andre: for the singletons, don't design singletons. Maybe someone really wants to create another object. I would try to do something similar as has been done for Serial. Instead of creating your own instance of Serial, there is just Serial, Serial1, Serial2 as predefined objects (don't exactly know how they did that) Oct 21, 2022 at 15:07
0

Also, Using Static Methods Or Non-Member Functions (C-Style)

class MyClass {
  public:
    static void onMsg(int num1, int num2) {
      // NOTE: Can't call any non-static method functions here!
    }
};

class LibraryClass {
  using CallBack = void( * )(int, int);
  private:
    CallBack callBack;
  public:
    void Setup(CallBack callBackFunc) {
      callBack = callBackFunc;
    }
    void Loop() {
      callBack(1, 2);
    }
};

MyClass myClass;
LibraryClass libraryClass;

void setup() {
  libraryClass.Setup(&myClass.onMsg); // Provide the instance and function to call
}

void loop() {
  libraryClass.Loop();
}
1
  • 1
    Is there a difference between &myClass.onMsg and MyClass::onMsg? I don't think this provides an instance for the function to call. Otherwise you could call member functions and it needn't be static. (Side note: myClass with lower case m is not a class, it's an object; same for libraryClass) Oct 19, 2022 at 17:48

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.