Ricardo Reis
Ricardo Reis

Reputation: 89

Pass an object function by reference

I'm trying to pass an object function by reference. I can only make it work with a simple function.

Code:

class Light {

  ...... omitted some code here, not relevant .......

  void toggle2() {
    Serial.println("TOGGLE");
  };
}
class Button {
  public:
    OneButton pin;
    void single();

    Button(int _pin, void (&_single)() ){
      pin = OneButton(_pin, false, false);

      _single();
      pin.attachClick(_single);
    }
};
Light lights[] = {
  Light("Cozinha", CONTROLLINO_A15, CONTROLLINO_R0)
};
Button buttons[] = {
  Button(CONTROLLINO_A0, lights[0].toggle2)
};

the code above gives an error on button declaration

no instance of constructor "Button::Button" matches the argument list -- argument types are: (int, void ())

toggle2 function is a void function but maybe the program is confusing because of the Light type?

If I make the code with a simple function it works well, like this:

void toggle() {
  Serial.println("TOGGLE");
};

Button buttons[] = {
  Button(CONTROLLINO_A0, toggle)
};

Any suggestion??

Upvotes: 1

Views: 118

Answers (1)

user4581301
user4581301

Reputation: 33932

I've removed all of the noise from the code sample to focus on the how-tos of passing and calling a function pointer.

class Light {
public:
  void toggle2() {
  };
};
class Button {
  public:
    Button(int _pin, 
           void (Light::*single)(),  // the function. Compiler needs to know the class 
                                     // the function belongs to or it'll assume a free 
                                     // function
           Light& light) // class instance to invoke the member function on
    {
        (light.*single)(); // note the weird-looking invocation syntax.
                           // all of it is essential and easily <expletive deleted>ed up.
                           // C++FAQ recommends using a macro to keep from leaving 
                           // anything out. They don't recommend macros lightly.
                           // note you will want to store these two variables as Button 
                           // class members rather than using them immediately.
                           // I did the call in the constructor to keep the example simple.
    }
};

Light lights[] = {
  Light()
};

Button buttons[] = {
  Button(1, // the pin
         &Light::toggle2, // address of the fully-qualified member function 
         lights[0]) // instance to invoke member function on
};

The gory details, and some very good suggestions, can be found at Pointers to Member Functions

Note that since Button must now carry around the a reference to the Light instance it uses, making lights and Buttons local variable variables in a sufficiently widely scoped function is an attractive option.

For completeness, here's what it looks like with std::function and a Lambda Expression.

#include <functional>

class Light {
public:
  void toggle2() {
  };
};
class Button {
  public:
    Button(int _pin, 
           std::function<void()> single){ // std::function contains the necessary 
                                          // instance reference
        single(); // no muss, no fuss function call
    }
};
Light lights[] = {
  Light()
};
Button buttons[] = {
  Button(1, 
         [](){lights[0].toggle2();}) // lambda expression wrapping the function call
};

Upvotes: 1

Related Questions