Functions as arguments? Help!

Hello and thanks for reading.

I'm trying to pass a function with arguments as argument to another function. But I keep getting the error "Invalid use of void expression".

When the function I try to pass has no arguments, the code compiles, error free:

int value;

void function3();
void function2();
void function1(void (*func2)(), void (*func3)(), int value);


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

}
void loop() {
    function1(function2, function3,7000);
}

void function1(void (*func2)(), void(*func3)(), int value){
     
     (*func2)();
     delay(value);
     (*func3)();
     delay(value);      
}

void function2() {
    
    Serial.println("func2");
}
void function3() {
    
    Serial.println("func3");
}

When function2 has arguments, I get the error message.

String text;
int value;


void function3();
void function2(String text, int value2);
void function1(void (*func2)(String text, int value2), void (*func3)(), int value);


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

}




void loop() {
    function1(function2("test", 7), function3,7000);
}

void function1(void (*function)(), void(*other)(), int value){
     
     (*other)();
      delay(value);
      (*function)();
            
     
      
}

void function2(String text, int value2) {
    
    Serial.println(text);
    Serial.println(value);
}
void function3() {
    
    Serial.println("func 3");
}

I'm very new to programming, and this is my first time trying to pass a function as an argument. If I can get this working my design will, in general, be so much more elegant.

I need your expertise! Thank you in advance, oh kind Arduino community!

Sheesh, I've programmed since the '70s and never passed a function as a parameter. You may not want to start at the deep end. The best code is typically simple and boring. Never clever. What are you trying to do? In english.

-jim lee

You need to consider that passing the “String” type by reference * I don’t think you can pass it to a function in any other way.

Look at this code and the link at the bottom see if you can get closer to what you are trying I will keep an eye for your further questions.

Example of callbacks functions
To give you some working code that uses function Callbacks like crazy, I’ve been working on this for the past few days and I am at a point where it is working great (Not fully tested)!
I am happy to share it with you :slight_smile:

My Libraries are attached ( You are the first to see this, Documentation isn’t great yet )
Sketch:

#include "Interrupts.h"
InterruptsClass Interrupt;
void PinCB(uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
  sei(); // re enable other interrupts at this point,
  Interrupt.PinCallBack(Time, PinsChanged, Pins);
}
void Pin4CB(uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
  if (Interrupt.CheckPin(4)) { // rising
    Serial.print(" Pin 4 Rising \t");
  } else { // Falling
    Serial.print(" Pin 4 Falling \t");
  }

}

void setup() {
  Serial.begin(115200); 
  pinMode(7, OUTPUT);
  Interrupt.onInterrupt(PinCB); // Callback function passed here
  Interrupt .onPin(4, INPUT_PULLUP, Pin4CB); // Callback function passed here
}

void loop() {
  // put your main code here, to run repeatedly:
}

Short Pin 4 to ground and see what happens Serial Port is set to 115200
This is the most basic setup of the interrupt library :slight_smile:
also I asked a similar question in this post about a month ago
https://forum.arduino.cc/index.php?topic=436961.msg3010911#msg3010911

Z
PS. I know… Never use Serial.print() within an interrupt This is just and example and testing!!!

Interrupts.cpp (4.57 KB)

Interrupts.h (1.78 KB)

jimLee:
Sheesh, I've programmed since the '70s and never passed a function as a parameter. You may not want to start at the deep end. The best code is typically simple and boring. Never clever. What are you trying to do? In english.

-jim lee

Haven't you used attachInterrupt()? What does it take as a parameter? a function!

godivaPrima:
Does this count, passing the result of an analogRead() to Serial.print()?

Serial.print(analogRead(somePinOrOther));

No, sorry Analog read completed returns a value, then the value only is passed to Serial.print that then uses it.
What we are doing here is bring a callback function pointer into the function so it can be used at will by this function even providing values for the callback function we captured to use.

zhomeslice:
Haven’t you used attachInterrupt()? What does it take as a parameter? a function!

Ok ok, attachInterrupt(), …forgot that one…

-jim lee

The invalid use of void expression is due to you declaring a void (nothing) return from your function3 and function2 and then putting them into the call of function1.
since they are defined as returning void (nothing) it is a nonsensical attempt to do something with nothing

void function3();  // no return value
void function2();  // also - no return value

// now, a call to function1 with NO VALUES passed as arguments!!
void function1(void (*func2)(), void (*func3)(), int value);

Dan95:
The invalid use of void expression is due to you declaring a void (nothing) return from your function3 and function2 and then putting them into the call of function1.
since they are defined as returning void (nothing) it is a nonsensical attempt to do something with nothing

void function3();  // no return value

void function2();  // also - no return value

// now, a call to function1 with NO VALUES passed as arguments!!
void function1(void (*func2)(), void (*func3)(), int value);

You completely misunderstand the meaning of that code… There is absolutely nothign wrong with passing a pointer to a function with no return value. It is done all the time, just one obvious example being attachInterrupt().

Regards,
Ray L.

function1(function2("test", 7), function3,7000);

You appear to want to pass not just the pointer for function2, but also an arbitrary set of arguments to go with it. This is not an easy task, and you are way out of your depth trying to do that.

The Standard Template Library has a header that has the std::bind function, which does what you want. Unfortunately, the STL is not officially implemented for AVRs. There is a port online, but I can’t remember if it has functional implemented or not.

When you do it like this, function2 will be evaluated, and then its return value will be passed as function1’s first argument. function2 returns void, which you can’t use as an expression. That is why you get the error message about “inappropriate use”.

For now, if you want to pass argument values for function2, you need to make them separate arguments in function1.

void function1(void (*func2)(String text, int value2), String func2arg1, int func2arg2, void (*func3)(), int value);

Here is the most basic example I can type up
Hope this helps

static void nothing(void) {};
typedef void (*voidFuncPtr)(uint32_t,uint32_t,uint32_t);// Create a type to point to a funciton.
voidFuncPtr _status_cb = nothing;
void onCall(void (*CB)(unsigned long,unsigned long,unsigned long))
{
    _status_cb = CB;
}

void statusCall(unsigned long A,unsigned long B,unsigned long C)
{
    if(_status_cb) {
        _status_cb(A,B,C);
    }
}
void TestCallBack(unsigned long X,unsigned long Y,unsigned long Z){

  Serial.print(X);
  Serial.print(" ");
  Serial.print(Y);
  Serial.print(" ");
  Serial.print(Z);
  Serial.println();
}

void setup()
{ 
  
  Serial.begin(115200);
  Serial.println("Hello");
  onCall( TestCallBack );
}
void loop(){
 statusCall(10,20,30);
 delay(1000);
}

This:

void function1(void (*func2)(String text, int value2), void (*func3)(), int value);

says function1 takes three arguments:

the first argument is a POINTER to a function that takes two arguments
the second argument is a POINTER to a function that takes no arguments
the third argument is an int

This:

function1(function2("test", 7), function3,7000);

is calling function1, and passing three arguments:

the first argument is void - i.e. the return value obtained by invoking function2
the second argument is a POINTER to function3
the third argument is an int

So, your definition of function1, and your invocation of function1 do not agree. You would call function1 like this:

function1(function2, function3,7000);

This line does NOT invoke either function2 or function3. It simply passes pointers to them, so that function1 can evenutally de-reference those pointers, provide appropriate arguments, and invoke function1 and function2 like this:

void function1(void (*func2)(String text, int value2), void (*func3)(), int value)
{
    ....
    func2("test", 7);  // or, equivalently: (*func2)("test", 7);
    ....
    func3();  // or, equivalently: (*func3)();
    ....
}

Regards,
Ray L.

Ray -- Did you mean to change the argument names in your function1 from func2 and func3 (in the arg list) to function2 and function3 (in the function)?

TheCityGame:
Ray -- Did you mean to change the argument names in your function1 from func2 and func3 (in the arg list) to function2 and function3 (in the function)?

Oops! Fixed! Thanks for catching that.

Regards,
Ray L.

Thanks to everyone who replied, each answer was a valuable part of the whole discussion and contributive to my learning.

I tried this suggestion with success:

Jiggy-Ninja:
For now, if you want to pass argument values for function2, you need to make them separate arguments in function1.

void function1(void (*func2)(String text, int value2), String func2arg1, int func2arg2, void (*func3)(), int value);

Full code:

int value;
int value2;
String text;

String func2ard1;
int func2ard2;

void function3();
void function2(String text, int vlaue2);
void function1(void (*func2)(String func2arg1, int func2arg2),String func2arg1, int func2arg2, void (*func3)(), int value);


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

}
void loop() {
    function1(function2, "test", 7, function3,7000);
}

void function1(void (*func2)(String func2arg1, int func2arg2), String func2arg1, int func2arg2, void(*func3)(), int value){
     
     (*func2)(func2arg1, func2arg2);
     delay(value);
     (*func3)();
     delay(value);      
}

void function2(String text, int value2) {
    
    Serial.println(text);
    Serial.println(value2);
}
void function3() {
    
    Serial.println("func3");
}

I guess I’ll need to look into the nature of this std::bind function and find a port for AVRs if I want to pass it arbitrary arguments. Thanks Jiggy!

Jiggy-Ninja:
You appear to want to pass not just the pointer for function2, but also an arbitrary set of arguments to go with it. …

The Standard Template Library has a header that has the std::bind function, which does what you want. Unfortunately, the STL is not officially implemented for AVRs. There is a port online, but I can’t remember if it has functional implemented or not.