Store member- or lamda-functions into a member variable

I was wondering can i somehow save a lamda or a member function into a member variable .
E.g in a class like below and how can I call it again?
(Task contains some basic functions and the virtuall function execute. The StoreFunction is a placeholder for the solution how I can store the desired function)

This is how I would like to

#include <Arduino.h>
#include <Task.h>

#pragma once

class SimpleTask :public Task
{
  
public:
  SimpleTask(){}
  ~SimpleTask(){}
  // Set the desired function, could be in the construktor too
  void setTask(StoreFunction function)
  {
    storedFunction = function;
  }
  void execute()
  {
    // execute the stored function
    storedFunction();
  }


protected:
  // Stored function
  StoreFunction storedFunction;
  
};

I need to be able to save this class in to an Task* datastructure so using an template class to store the function didn´t work for me. Are there ways like saving different lamda functions or member functions to a member variable or is that not possible?

Post Task.h
What will call 'setTAsk()'?
What will be the type of the passed function ... i.e. what type is 'StoreFunction'?
Show an example usage.

#include <iostream>

void hw(void) { std::cout << "Hello world!\n"; }

struct task {
    task(void (*f)(void)) { f_ = f; }
    void operator() () { (f_)(); };
protected:
    void (*f_)(void);
};

int main() {
    task t(hw);
    t();
}

For some reason, I had trouble compiling for Teensy 3.2 with iostream. But after switching to the Arduino Serial object, the code you posted compiles and runs as expected.

#include "Arduino.h"

void hw(void) {
	Serial.println("Hello world!");
}

struct task {
	task(void (*f)(void)) {
		f_ = f;
	}
	void operator()() {
		(f_)();
	}

protected:
	void (*f_)(void);
};

void setup() {
	Serial.begin(115200);
	delay(3000);

	task t(hw);
	t();
}

void loop() {
}

Serial Monitor:

Hello world!

Does that work with lamda functions or member functions too? I tried something similar from this before but could get it working as I need it.

Post a usage case / example of what you want that scenario to look like.


struct task 
{
	task(void (*f)(void)) 
  {
		f_ = f;
	}
	void operator()() 
  {
		(f_)();
	}

protected:
	void (*f_)(void);
};

void setup() 
{
	Serial.begin(115200);

  auto hw = []() -> void 
  {
    Serial.println("Hello world!");
  };
  
	task t(hw);
	t();
}

void loop()
{
}

Since I don not know how to do that I only can describe it.

I have a class called e.g. VButton . This class has to do multiple tasks which needs to get executed in a specific order on a specific time. So the class defines different tasks.

  1. Task: Switch button on (Time to get executed)
  2. Task: Switch button off (Time to get executed)
    These Tasks are handled by an Scheduler.
    But since the Scheduler should handle mutch different tasks it also should be able to execute other tasks like:
    Task Transmitdata
    Task Read sensor
    etc
    (From other classes)

So the idea is that the simple task can save a member or lamda-function which can access the variables of the object it came from and execute that methode. So that many different classes can use this SimpleTask class for that.

Do you know what I mean?

Still a not very well-defined problem statement.

But, since SimpleTask seems to inherit from Task, perhaps the Task class class should define a virtual function that's overloaded by all the classes that inherit from it. Then your (unposted) VButton class would call that.

Does the called function even know whether the caller used a lambda function or a normal function ptr?

Try this out:

static void nothing(uint32_t,uint32_t,uint32_t) {};
typedef void (*voidFunc3u32Ptr)(uint32_t,uint32_t,uint32_t);// Create a type to point to a function.
voidFunc3u32Ptr  CallBack= nothing;

void setup() {
 Serial.begin(115200); //115200
 CallBack= [](uint32_t a,uint32_t b,uint32_t c){
    Serial.println(a+b*c);
    delay(500);
  };

}
void loop() {
  CallBack(1,2,3);
}

And to have more FUN an Array of Stored Lamda Functions :slight_smile:

static void nothing(uint32_t, uint32_t, uint32_t) {};
typedef void (*voidFunc3u32Ptr)(uint32_t, uint32_t, uint32_t); // Create a type to point to a function.
voidFunc3u32Ptr  CallBack[3] = {nothing, nothing, nothing};

void setup() {
  Serial.begin(115200); //115200
  CallBack[0] = [](uint32_t a, uint32_t b, uint32_t c) {
    Serial.println(a + b * c);
    delay(500);
  };
  CallBack[1] = [](uint32_t a, uint32_t b, uint32_t c) {
    Serial.println(a * b * c);
    delay(500);
  };
  CallBack[2] = [](uint32_t a, uint32_t b, uint32_t c) {
    Serial.println(a - b / c);
    delay(500);
  };
}
void loop() {
  for (int i = 0; i < 3; i++) {
    Serial.print("PinCB[");
    Serial.print(i);
    Serial.print("] =");
    CallBack[i](10, 2, 3);
  }
}

Z

you can store regular functions too as long as the function definition matches

static void nothing(uint32_t, uint32_t, uint32_t) {};
typedef void (*voidFunc3u32Ptr)(uint32_t, uint32_t, uint32_t); // Create a type to point to a function.
voidFunc3u32Ptr  PinCB[3] = {nothing, nothing, nothing};

// This is just storing a function like
void TestFunction(uint32_t a, uint32_t b, uint32_t c) {
    Serial.println(a - b + c);
    delay(500);
}
void setup() {
  Serial.begin(115200); //115200
  PinCB[0] = [](uint32_t a, uint32_t b, uint32_t c) {
    Serial.println(a + b * c);
    delay(500);
  };
  PinCB[1] = [](uint32_t a, uint32_t b, uint32_t c) {
    Serial.println(a * b * c);
    delay(500);
  };
  PinCB[2] = TestFunction;
}
void loop() {
  for (int i = 0; i < 3; i++) {
    Serial.print("PinCB[");
    Serial.print(i);
    Serial.print("] =");
    PinCB[i](10, 2, 3);
  }
}

Z

It is almost what I am searching the problem is that I can not store member-functions with it (only free functions).
The same goes for the lamda I need to use some Variables of the class.

I modified my SimpleTask class to this:

#include <Arduino.h>

#pragma once

typedef void (*StoreFunction)();

class SimpleTask
{

public:
  SimpleTask(){}
  ~SimpleTask(){}
  // Set the desired function, could be in the construktor too
  void setTask(StoreFunction function)
  {
    this->storedFunction = function;
  }
  virtual int execute()
  {
    storedFunction();
    return 0;
  }

protected:
  // Stored function
  StoreFunction storedFunction;
};

And it should be called like this:

#include <Arduino.h>
#include <SimpleTask.h>

#pragma once

class TaskTest
{
public:
    TaskTest(){

    };
    ~TaskTest(){};

    void saveThisFunction()
    {
        Serial.print(myVariable);
    }
    void test()
    {

        SimpleTask sTask;

        sTask.setTask([this]()
                      {
        Serial.println(myVariable);
        delay(500); });

        SimpleTask sTask2;
        sTask2.setTask(saveThisFunction)
    };

protected:
    String myVariable = "This is a test.";
};

But I am not able to do that because of the "this" and the bound function. ( I only need one to work, but if bouth are working it would be even better).

I can't test compile any possible solutions. Not enough information provided.

Where is the definition of the class 'Task'?

Where is 'TestClass.h'?

Maybe you're looking for something like this?

#include "Arduino.h"
#include <functional>

using fnPtr = std::function<void()>;

class ClassA {
    friend class ClassB;
  private:
    void storeFunction(fnPtr f) {
      ptr = f;
    }

    void run() {
      ptr();
    }

    fnPtr ptr = nullptr;
};

class ClassB {
  public:
    ClassB(uint8_t i) : id(i) {
      objectA.storeFunction(std::bind(&ClassB::printId, this));
    }

    void run() {
      objectA.run();
    }

  private:
    uint8_t id;
    ClassA objectA;

    void printId() {
      Serial.println(id);
    }
};

void setup() {
  Serial.begin(115200);
  delay(1000);

  ClassB b1(1);
  ClassB b2(2);

  b1.run();
  b2.run();
}

void loop() {
}

Just ignore them they are just classes which are used in my program, but not needed here. I forgot to remove them. The Task class just provides a common interface for other classes who need to run a more complex task. The same goes for the TestClass.
The problem is the storing of the member function or lamda function with "this".

You can use std in Arduion? Or is that a reimplementation of the normal std?

class  Task
{
public:
  Task();
  ~Task();
  virtual int execute()=0;
}

You can with Arduino boards based on ARM, ESP8266, and ESP32 processors.

take a look at my library interrupt.cpp and interrupts.h located here ZHomeSlice/Arduino_Interrupts

I store functions created in the .ino file and store them for each time the pin change events occur.
see:

InterruptsClass & InterruptsClass::onInterrupt(void (*CB)(uint32_t,uint32_t,uint32_t)){
	Int_CB = CB;
	return *this; // return Pointer to this class
}

InterruptsClass & InterruptsClass::onPin(int Pin, int InputType, void (*CB)(uint32_t,uint32_t,uint32_t)){
	AddPin( Pin,  InputType);
	PinCB[Pin] = CB;
	return *this; // return Pointer to this class
}

then when the pin changes state I run the provided functions

void ISR_Exe(){
	_Pins.Port[0] = PIND; // 0-7
	_Pins.Port[1] = PINB; // 8-13
	_Pins.Port[2] = PINC; // A0-A6
	uint32_t _cTime = micros();    
	_Pins.Port[3] = 1;    // Placeholder
	uint32_t _Changed = _Pins.All ^ _PCintLast;// doing a ^ between the current interruption and the last one indicates which pin changed
	uint32_t _Masked = _Changed & _PinMask;// Has one of the pins we need changed
	if(!_Masked)return;                    //No Data move on
	if (Int_CB) {
		Int_CB(_cTime, _Masked,_Pins.All); // Callback Function
	}
	Interupted++;
	_PCintLast = _Pins.All;          // we memorize the current state of all PINs in group
}

and

void InterruptsClass::PinCallBack(uint32_t Time, uint32_t PinsChanged, uint32_t Pins){
	for(byte Pin = 0;Pin<20;Pin++){
		if(bitRead(PinsChanged, Pin)){
			if (PinCB[Pin]) {
				PinCB[Pin](Time,PinsChanged,Pins);
			}
		}
	}
}

my header file is basically this:

class InterruptsClass {
public:
	//	 Interrupts();
	uint8_t  BitNumber[20] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21}; // Key = Pin Val = Position List of pins that could be used as ping inputs:
	static void nothing(void) {};
	static void nothing(uint32_t,uint32_t,uint32_t) {};

	typedef void (*voidFuncVoidPtr)(void);
	typedef void (*voidFunc3u32Ptr)(uint32_t,uint32_t,uint32_t);// Create a type to point to a function.
	voidFunc3u32Ptr  PinCB[20] = {nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing}; // Key = Pin Val = Position List of pins that could be used as ping inputs:
	InterruptsClass & AddPin(uint8_t pin, uint8_t InputType = INPUT);
	InterruptsClass & onInterrupt(void (*CB)(uint32_t,uint32_t,uint32_t));
	InterruptsClass & onPin(int Pin, int InputType, void (*CB)(uint32_t,uint32_t,uint32_t));
	void PinCallBack(uint32_t Time, uint32_t PinsChanged, uint32_t Pins);
	bool CheckPin(uint8_t Pin);
	InterruptsClass & Timer0Enable();
	InterruptsClass & onTimer0(void (*CB)(void));
	InterruptsClass & Timer1Enable();
	InterruptsClass & Timer2Enable();
	// *************************************************************
	volatile uint32_t EdgeTime[20]; // Time Storage
	// RC Remote
	volatile uint16_t RCRemote(uint8_t Pin, uint32_t cTime, uint32_t Pins,bool ValidRC = true);
	// Ping
	volatile int16_t Ping(uint8_t Pin, uint32_t cTime, uint32_t Pins);
	// Encoder
	volatile int8_t Encoder(uint8_t ClockPin, uint8_t DataPin, uint32_t Pins,bool HalfStep);
	// Switch
	volatile int8_t Switch (uint8_t Pin,uint16_t cTime,int16_t debounceTime,bool OnRise);
	// Sample Time
	volatile uint16_t SampleTime(uint8_t Pin, uint32_t cTime, uint32_t Pins,bool OnRise);
	volatile uint16_t DelayTimer(uint8_t Pin, uint32_t cTime, uint32_t Pins,bool OnRise);
	volatile int32_t Tachometer(uint8_t ClockPin, uint8_t DataPin, uint32_t cTime, uint32_t Pins);
};

The .ino file setup function looks like this:

void setup() {
  Serial.begin(115200); //115200
  Serial.println(" testing");
  // put your setup code here, to run once:
  pinMode(9, OUTPUT);
  Interrupt.onInterrupt([](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    sei(); // re enable other interrupts at this point,
    Interrupt.PinCallBack(Time, PinsChanged, Pins);
  });
  Interrupt.onPin(4, INPUT_PULLUP, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    if (Interrupt.CheckPin(4)) { // rising
      Serial.println(" Pin 4 Rising \t");
    } else { // Falling
      Serial.println(" Pin 4 Falling \t");
    }
  });
  Interrupt.onPin(5, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    uint16_t RCTime =  Interrupt.RCRemote(5, Time, Pins, true);
    if (RCTime) {
      Serial.print(" RC Time:");
      Serial.print(RCTime);
    }
  });
  Interrupt.onPin(6, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    unsigned int PingTime = Interrupt.Ping(6, Time, Pins); 
    if (PingTime) {
      Serial.print("Ping \t");
      Serial.print(microsecondsToCentimeters(PingTime));
      Serial.println("cm");
    }
  });
  Interrupt.onPin(9, INPUT_PULLUP, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Serial.print("Switch \t");
    Serial.println(Interrupt.Switch(9, Time, 1000, false));

  });
  Interrupt.onPin(10, INPUT_PULLUP, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Serial.print("Switch \t");
    Serial.println(Interrupt.Switch(10, Time, 1000, false));

  });
  Interrupt.onPin(11, INPUT_PULLUP, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Serial.print("Switch \t");
    Serial.println(Interrupt.Switch(11, Time, 1000, false));

  });
  Interrupt.onPin(12, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    EncoderCounter += Interrupt.Encoder(12, 13, Pins, true);
    Serial.print("Count ");
    Serial.print(EncoderCounter);
    Serial.print("\t Encoder \t");
    Serial.println(Interrupt.Encoder(12, 13, Pins, true));

  });
  Interrupt.onPin(13, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    EncoderCounter -= Interrupt.Encoder(12, 13, Pins, true);
    Serial.print("Count ");
    Serial.print(EncoderCounter);
    Serial.print("\t Encoder \t");
    Serial.println(-1 * Interrupt.Encoder(12, 13, Pins, true));
  });
}

I call the function of the class within the Lambda function

unsigned int PingTime = Interrupt.Ping(6, Time, Pins);

I can only show you examples of my successes as I'm not fully understanding your question.
Z

My question is how I can save bound (member)-functions not free functions to a variable in a Storageclass.

So you just can write your class and define some SimpleTask objects which can save an member-function of the class you are writeing. But it is important that is works with other classes too with out modifying SimpleTask when there is a new class which uses the SimpleTask class.

e.g you got a class car

class  Car
{
  
public:
  Car()
{
SimpleTask s1;
s1.setTask(stop);
SimpleTask s2;
s2.setTask(stop);
Controller::add(s1);
Controller::add(s2);

}

  ~Car(){}
void stop()
{
  speed=0;
}

void drive()
{
  speed=50;
}
protected:
   int speed=0;
};

and a class trafficlight

class  TrafficLight
{
  
public:
  TrafficLight()
{
SimpleTask s1;
s1.setTask(red);
SimpleTask s2;
s2.setTask(yellow);
SimpleTask s3;
s3.setTask(green);
Controller::add(s1);
Controller::add(s2);
Controller::add(s3);
}
  ~TrafficLight(){}
void green()
{
  color='g';
}

void yellow()
{
  color='y';
}

void red()
{
  color='r';
}
protected:
   char color='r';
};

And then the controller just calls the functions (some thing like that)

Controller::runNextTask()

Every methode of this classes should be defines as tasks which get managed by a controller which says which should get executed on when. So the controller has a list of (Simple)Tasks which the controller then calles. Every class should be able to create this SimpleTask Objects and fill it with member functions.