0

I'm new to c++ and I'm trying to make a generic switch (i.e. the device, not the C++ statement) that could be used to blink lights, turn beeps on and off, etc, in my Arduino project.

I could create a switchable interface and implement that in the classes that I want to "switch". But since I'm doing it as study purposes and I saw the pointer-to-functions ability in C++ (that is new to me since I come from C# and Java), I tough it would be a good opportunity to give it a try...

The problem is that I can pass the function in my code only if it's a local function but it won't work if I try to pass a function from another object like a led for example.

Some code to illustrate the problem. This is the switch.cpp, it recieves the On and Off functions in it's constructor and it has a update method that is called inside the loop method in the Arduino ino main class:

auto_switch.cpp

using switch_function = void(*)();
auto_switch::auto_switch(const switch_function on_function, const switch_function off_function, const int max_speed_count)
{
    //sets all variables...
}

void auto_switch::update(const unsigned long millis)
{
    //turn switch on and off...
}

And this is my ino file

ino file

#include <Arduino.h>
#include "led.h"
#include "auto_switch.h"

led* main_led;
auto_switch* led_switch;
int slow_speed;

//ugly code
void turn_led_on()
{
    main_led->turn_on();
}

//ugly code
void turn_led_off()
{
    main_led->turn_off();
}

void setup() {
    main_led = new led(2, 3, 4, true, color::white);

    //ugly code
    led_switch = new auto_switch(turn_led_on, turn_led_off, 3);

    slow_speed = led_switch->add_speed(100, 100, 3, 1000);
    led_switch->set_active_speed(slow_speed);
    led_switch->turn_on();
}

void loop() {
    led_switch->update(millis());
}

It works but I had to make a local function (turn_led_on and turn_led_off) to be able to assign the inner functions as a parameter to the auto_switch constructor, the parts that I've wrote //ugly code

I wanted to do something like this, without the glue code in between:

//doesn't work
led_switch = new auto_switch(main_led->turn_on, main_led->turn_off, 3);

Is it possible? I've read something about static pointer to function and some std functions that help with that, if I get it right the glue code is necessary in this case so that the compiler can know where the functions are coming from I guess (from which object), but since the functions I need to call cannot be static I've discarded this option, and the std functions I believe it can't be used with the Arduino or could but shouldn't for performance limitations...

Anyway, does it make sense, can it be done using pointer to functions or should I create a interface or something different?

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Does this answer your question? [Function pointer to member function](https://stackoverflow.com/questions/2402579/function-pointer-to-member-function) – Scheff's Cat Jan 12 '20 at 17:48
  • @Scheff thanks for the suggestion, I've read about member functions but I believe it wouldn't make the switch generic right? I mean, the switch would only work with the member type so I would need one switch classe for every switch target like led, beeps etc right? – Jheyson Bicudo de Lima Jan 12 '20 at 17:55
  • If you write "switch" you don't mean the statement `switch`, don't you? Sorry, that's a bit confusing. However, if you are looking for something more generic which can hold pointers to plain functions as well as member function pointers (with object) I suggest to have a look into [std::function](https://en.cppreference.com/w/cpp/utility/functional/function). – Scheff's Cat Jan 12 '20 at 17:57
  • if that's right it would be better to use an interface instead I guess?! – Jheyson Bicudo de Lima Jan 12 '20 at 17:58
  • sorry, yes, when I say switch I'm talking about a class called switch that has the ability to call on and off on the target to control lights and so on,,, not the switch case statement.... – Jheyson Bicudo de Lima Jan 12 '20 at 18:00
  • @Scheff, about the std::function, I don't know if it's possible to use it with the Arduino, maybe I'm wrong but it seems that it's too heavy for a arduino nano for exemple,,, but I'll look into it... – Jheyson Bicudo de Lima Jan 12 '20 at 18:03
  • just reading more about it std::function uses dynamic memory allocation that is really heavy or impractical for arduino hardware limitations... – Jheyson Bicudo de Lima Jan 12 '20 at 18:07
  • @JheysonBicudodeLima I've written code for Arduino which uses dynamic memory allocation. I don't have an Arduino myself, but the user of the code didn't report any problems. Although `std::function` has some overhead compared to raw member function pointers, it's supposed to be lightweight and I doubt you'll have any problems with it. – Ted Lyngmo Jan 12 '20 at 18:43
  • 1
    @TedLyngmo it's good to know that, I'll give it a try then, thanks for the info – Jheyson Bicudo de Lima Jan 12 '20 at 20:54

1 Answers1

0

Before deciding how to do it, the qquestion is what do you want to do and why. Because, maybe there are better alternatives using simple C++ idioms.

Option 1: specialization with polymorphism

Do you want to specialize some functions of your switch, so instead of calling the function of the auto_switch you'd call dome more specialized ones ?

In this case you wouldn't do:

//doesn't work
led_switch = new auto_switch(main_led->turn_on, main_led->turn_off, 3);

but instead you would rely on polymorphism with virtual functions in the base class:

class auto_switch {
...
    virtual void turn_on();  
    virtual void turn_off(); 
...
}; 

and write a specialized class for the leds:

class led_witch : public auto_switch {
...
    void turn_on() override;  
    void turn_off() override; 
...
}; 

In fact, the compiler will generate some function pointers behind the scene, but you don't have to care:

auto_switch s1=new auto_switch(...); 
auto_switch s2=new led_switch(...);   // no problem !! 

s1->turn_on();     // calls auto_switch::turn_on() 
s2->turn_on();     // calls led_switch::turn_on() since the real type of s2 is led_switch 

But event if each object's behavior is dynamic on the the base of the real class of the object, the objects of the same class share a behavior that was predefined at compile time. If this is not ok, go to the next option.

Option 2: the member function pointer

The functions of another objects can only be invoked with that object at hand. So having a function pointer to a led function is not sufficient: you also need to know on which led it shall be applied.

This is why member function pointers are different and somewhat constraint: you can only invoke functions of class of your member function pointer. If polymorphism is sufficient (i.e. if derived class has a different implementation of a function already foreseen in the base classe) then you are lucky. If you want to use a function that only exists in the derived class and not in the base class, it won't compile.

Here a simplified version of auto_swith: I provide a function, but allso a pointer to the object on which the function has to be invoked:

class auto_switch{
    void (led::*action)(); 
    led *ld; 
public: 
    auto_switch(void(led::*a)(), led*l) : action(a), ld(l) {}
    void go () { (ld->*action)(); }
}; 
// usage:  
auto_switch s(&led::turn_off, &l1);
s.go(); 

Online demo

Option 3 : the functional way (may that's what you're looking for ?)

Another variant would be to use the standard functional library to bind a member function and the object on which it shall be executed (as well as any need parameters):

class auto_switch{
    std::function<void()> action; 
public: 
    auto_switch(function<void()>a) : action(a) {}
    void go () { action(); }
}; 

Here you can bind anything: any function of any class:

auto_switch s(bind(&led::turn_off, l1));
s.go(); 
auto_switch s2(bind(&blinking_led::blink, l2));
s2.go(); 

Online demo

Option 4 : command pattern

Now if you want to perform something on an object when you turn on and off the switch, but you need total flexibility, you can just implement the command pattern : this lets you execute anything on any object. And you don't even need a function pointer.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Wow, first thanks for taking your time, it's a really detailed answer... I'll try to comment about the options you told me about: 1:In this case we'd be specializing the auto_switch, my ideia wasn't in heavily specialization but one generic switch with only one behavior that could switch multiple objects... basically the command pattern behavior (that I ended up doing anyway) 2:If I understand it right, doing it this way the auto_switch class gets heavily coupled with the led class, sure in some cases that's completely valid but in my case I wanted I generic switch... – Jheyson Bicudo de Lima Jan 12 '20 at 21:05
  • 3: I believe that the functional way would lead me to the result too but since I've read that I should avoid dynamic memory allocation on arduino it made me avoid that route, but I'll try it too for learning purposes. 4: Exactly the behavior I wanted... but since I'm learning cpp I thought "pointers, why not?" I ended up doing it using the pattern but since I'm studying cpp for the first time I wanted to make something different using the pointers way to learn how it works and I got stuck and was trying to make it work, but now I don't think its the best way to it... – Jheyson Bicudo de Lima Jan 12 '20 at 21:13
  • summarizing... I was trying to avoid the command pattern way to try to learn something new with function pointer (since the languages I'm used to doesn't have this option) and I though that I could achieve the same result without forcing other classes to implement my command interface by simply using pointer to functions, it would be a win win... If I got it right, the way to achieve that would be with option 3 and the simpler way would be option 4 with some coupling to my command interface... thansk again for your help @Christophe – Jheyson Bicudo de Lima Jan 12 '20 at 21:20