2

I am trying to pass a class internal function as a callback function within the class. The error behavior is similar to this problem. Whatsoever, I was unable to construct working code based on that question's answer.

I try to pass over a callback function from within the class,in order to create a finite state machine, inspired by the examples I found on adafruit regarding multithreading. The idea is, that each state can call his successor. The class itself then represents the state machine and I just will call its update function.

While the following code does not resemble this utility, it resembles the problem in a minimal example (based on this larger codepiece):

class MyClass
{
  const byte theAnswer = 42;
  void (*myCallback)();

  void setCallback(void (*callback)())
  {
    myCallback = callback;
  }

  public:

  void print_info()
  {
    Serial.println(theAnswer);
  }

  void call()
  {
    myCallback();
  }

   MyClass()
  {
    setCallback(&MyClass::print_info);
  }
};


MyClass AClassOfItsOwn();

void setup() 
{
  Serial.begin(9600);
  while (! Serial); // Wait until Serial is ready
}

void loop() 
{
  AClassOfItsOwn.call();
  return;
}

I get the following error code returned:

...testscript.ino: In constructor 'MyClass::MyClass()':

testscript:25: error: no matching function for call to 'MyClass::setCallback(void (MyClass::*)())'

     setCallback(&MyClass::print_info);

                                     ^

...\testscript.ino:25:41: note: candidate is:

...\testscript.ino:6:12: note: void MyClass::setCallback(void (*)())

   void setCallback(void (*callback)())

        ^

...\testscript.ino:6:12: note: no known conversion for argument 1 from 'void (MyClass::)()' to 'void ()()'

...\testscript.ino: In function 'void loop()':

testscript:40: error: request for member 'call' in 'AClassOfItsOwn', which is of non-class type 'MyClass()'

   AClassOfItsOwn.call();

                  ^

exit status 1 no matching function for call to 'MyClass::setCallback(void (MyClass::*)())'


fun times with polymorphism

This is code we discussed during looking for a solution. I leave it here for future reference.

I tried creating an instance of this wrapper within the actual class to hold the callback, but that did not work either.

class CallbackWrapper
{
  void (*myCallback)();
  public:

  void call()
  {
    myCallback();
  }

  void setCallback(void (*callback)())
  {
    myCallback = callback;
  }
};

class MyClass : public CallbackWrapper
{
  const byte theAnswer = 42;
  public:

  void print_info()
  {
    Serial.println(theAnswer);
  }

   MyClass()
  {
    CallbackWrapper::setCallback(&MyClass::print_info);
  }
};

trying to store the callback function in a wrapping class (also not working, '((MyClass*)this)->MyClass::CBW' does not have class type)

class CallbackWrapper
{
  void (*myCallback)();
  public:

  void call()
  { myCallback(); }

  void setCallback(void (*callback)())
  { myCallback = callback; }
};

class MyClass 
{
  const byte theAnswer = 42;
  CallbackWrapper CBW();
  public:
  void print_info() { Serial.println(theAnswer); }
  void call() { CBW.call(); }
  MyClass()
  { CBW.setCallback(&MyClass::print_info); }
};

An update on why I want to change the callback function during runtime. Here I try to randomly change the callback function:

class MyClass 
{
  CallbackWrapper CBW();
  public:
  void do_this() { Serial.println("doing this"); }
  void do_that() { Serial.println("doing that"); }
  void call() 
  { 
    CBW.call();
    if (rand() > 0.5)
    {
      CBW.setCallback(&MyClass::do_this)
    }
    else
    {
      CBW.setCallback(&MyClass::do_that)
    }
  }
   MyClass()
  {  }
};

For discussion with Michail

class CallbackWrapper
{
  public:
  void (*myCallback)();
  void call()
  { myCallback(); }

  virtual void setCallback(void (*callback)())
  {  }
};

class MyClass : public CallbackWrapper
{
  public:
  void setCallback(void (*callback)())
    { myCallback = callback; }
  void do_this() { Serial.println("doing this"); }
  void do_that() { Serial.println("doing that"); }
  void call() 
  { 
    CallbackWrapper::call();
    if (rand() > 0.5)
    { setCallback(&MyClass::do_this); }
    else
    { setCallback(&MyClass::do_that); }
  }
   MyClass()
  {  }
};
4
  • 1
    A member function does not have the prototype that you think. It has a hidden parameter for "this". A static member function could work as callback function but then it could not access member data. You should consider using virtual instead. The callback setting is then done by sub-class and implementation of the virtual member function. The C++ run-time will do the dispatch depending on the instance. Commented Jan 15, 2016 at 14:20
  • OK, I tried a bit with virtual functions, but I cannot make it run (see latest addition of code failure). Could you provide an example?
    – Faultier
    Commented Jan 15, 2016 at 14:40
  • Have a look at this github.com/mikaelpatel/Cosa/blob/master/examples/Blink/… and the FSM library github.com/mikaelpatel/Cosa/blob/master/cores/cosa/Cosa/FSM.hh. The state is a callback style function. Commented Jan 15, 2016 at 16:26
  • Without addressing the polymorphism part of the question, I have a post about calling an ISR from a class.
    – Nick Gammon
    Commented Jan 15, 2016 at 20:46

1 Answer 1

6

Unless you specifically limit yourself to one single class for a callback (i.e., include the class name in the function pointer specification) then you can ONLY use static member functions as callbacks.

static member functions appear as a normal function, not a member function, so don't have the implied MyClass *this as a first parameter.

If you want to allow member functions that aren't static you will have to make use of polymorphism and have a single base class that all classes which provide a callback function will inherit from. Then you limit the callback function pointer to be members of that base class.

It is possible to do it if you are happy to limit to just one class, though the syntax is rather cryptic:

class MyClass 
{
  const byte theAnswer = 42;
  void (MyClass::*myCallback)();

public:

  void call() {
    (this->*myCallback)();
  }

  void setCallback(void (MyClass::*callback)()) {
    myCallback = callback;
  }

  void print_info() {
    Serial.println(theAnswer);
  }

  MyClass() {
    setCallback(&MyClass::print_info);
  }
};

Using polymorphism you can only pass a callback function that is a member of the parent class, so it looks like that method may be out.

10
  • So, I tried creating a base class CallbackWrapper which has the callback functionality ((*myCallback)(), setCallback() and call()). I inherited MyClass from that base class. I tried to hook up my Callback using CallbackWrapper::setCallback(&MyClass::print_info()). Return is no matching function for call to 'MyClass::setCallback(void (MyClass::*)())'
    – Faultier
    Commented Jan 15, 2016 at 13:49
  • I can't visualize what you are describing. Polymorphism is a tricky subject and easy to get wrong.
    – Majenko
    Commented Jan 15, 2016 at 13:52
  • I abuse the edit question to post my futile try. (:
    – Faultier
    Commented Jan 15, 2016 at 13:57
  • That's not abuse - that's what it's there for. You're not using polymorphism in the right way - you need to think along the lines of an "interface" as Java has it. I'll see about an example.
    – Majenko
    Commented Jan 15, 2016 at 13:58
  • Actually, I am not sure even I can do it using polymorphism - it's incredibly tricky. Can you have just one standard function name that will always be the callback function in a class?
    – Majenko
    Commented Jan 15, 2016 at 14:08

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.