Problem C++ Creating Callback Function Pointer that takes Class Ref as Param

Hello,

I'm currently having some issues creating some callback functions in cpp to use within the ino file.

I have it working using void functions with no parameters, but as soon as I try to add params as part of the callback, it starts throwing invalid use of Void Expression errors. Not sure what is up at this point, but I'm sure I'm probably screwing up the function pointer syntax.

The use case:

I'm currently using some rotary encoders and created a class to set them up and track all variables relevant to each encoder etc. I would like to create a callback function that takes in a encoder class reference as a parameter, so that I can have a handful of functions for the encoder class rather than a function for each specific encoder.

What's working:

The simplified C++ Declaration:

Class Encoder
{
public:
    Encoder();

    void SetCallbackLeft (void (void));
    void SetCallbackRight (void (void));

private:
    
    void (*LeftTurn) (void) = NULL;
    void (*RightTurn) (void) = NULL;

C++ Definition:

void Encoder::SetCallbackLeft (void (*FuncPtr) (void) )
{
    // Function LeftTurn is called when some additional functions determine the encoder has
    // been turned counter counter-clockwise.
    // Using this as I am variably changing the response of various encoders depending on use.

    LeftTurn = FuncPtr;
}

void Encoder::SetCallbackRight (void (*FuncPtr) (void) )
{
    // Function RightTurn is called when some additional functions determine the encoder has
    // been turned counter clockwise.
    // Using this as I am variably changing the response of various encoders depending on use.

    RightTurn = FuncPtr

}

Within the INO:

Encoder Encoder1 ();

void setup()
{

Encoder1.SetCallbackRight(IncrementStoredVariable);
Encoder1.SetCallbackLeft(DecrementStoredVariable);
}

void loop()
{
Encoder1.EvaluateState();
}

void IncrementStoredVariable (void)
{
Encoder1.CurrentVarValue++;
}

All the above works fine. The problem is if I try to parametrize the ino function IncrementStoredVariable to take in any sort of parameters—let alone a reference to the invoking encoder, it throws an Invalid Use of Void Expression Error.

The goal is for me to just pass a reference of the invoking class instance to modify the variable as so.

INO

Encoder Encoder1 ();

void setup()
{

Encoder1.SetCallbackRight(IncrementStoredVariable(Encoder1));
Encoder1.SetCallbackLeft(DecrementStoredVariable(Encoder1));
}

void loop()
{
Encoder1.EvaluateState();
}

void IncrementStoredVariable (Encoder& TriggeringEncoder)
{

Encoder*  CallingEval = TriggeringEncoder
CallingEval.CurrentVarValue++;
}

I have tried adjusting the cpp declaration and definitions to account for the encoder reference; however, no matter what I put, it keeps throwing a void expression error. I tried changing the return type of the class pointer itself, and that just doesnt compile with any usable error data. To simplify things even further, I tried just doing with with a simple uint8_t and that wont work either, so I'm at a loss. The only error it shows is an invalid use of void expression pointed at the callback binding in the ino setup: nothing else. It even throws that as the error if I try to change the function pointers to be uint8_t. So it shouldnt even be a void expression anymore.

A void function can still take in a parameter and return void, but the Arduino IDE seems to disagree with this. Again, I'm sure I am doing something wonky with my function pointer syntax and class reference, but at this point I'm out of ideas. I really do not want to be creating duplicate increment and decrement functions for the amount of encoders that will be used as input controls.

-- EDIT--

I tried bolding the areas that were changed in the final code block and realized that didnt work the way I wanted. Removed extra * marks.

Yet you don't show the FULL TEXT of those errors ,which contains ALL of the details about what, where and why...

In absolute terms, Why would you do this?

Encoder1 knows itself, you would not need to pass that as a parameter

And to be more specific, the way you wrote it, the compiler tries to call IncrementStoredVariable with Encoder1 as parameter and pass the outcome of this function call to SetCallbackRight …. Of course this matches nothing at all…

if you really wanted to pass a parameter when you set the callback, you would write something like this

Encoder1.SetCallbackRight(IncrementStoredVariable, someParameter);

That is the SetCallbackRight function would have to expect two parameters

Side note:
This is not how you tell the parameter is a function pointer

It’s always easier to read when you define a type for your function pointer

typedef void (*t_callback)(void); // type for conciseness
…
void SetCallbackLeft(t_callback FuncPtr);

Forgot to add it, but as far as errors go, it is the least helpful because it is the same error no matter what return type i set the callback function to.

ino:54:46: error: invalid use of void expression
54 |  Encoder1.SetCallbackRight(IncrementStoredVariable(5));
     |                            ~~~~~~~~~~~~~~~~~~^~~

exit status 1

This is an example of the error output if I have the function pointer void taking an int parameter. This is also the same error if the function pointer returns an int taking an int parameter.
This is also the same error if I try the same two solutions above but parameterizing a reference to the invoking class.

ino:54:46: error: invalid use of void expression
54 |  Encoder1.SetCallbackRight(IncrementStoredVariable(Encoder1));
     |                            ~~~~~~~~~~~~~~~~~~^~~

exit status 1

hi @electricpixies ,
welcome to the forum..
your question confuses me some..
use call backs passing vars back and forth quite a bit lately..
some call back examples..

maybe it helps..
good luck.. ~q

See my answer… you are not passing 2 parameters, you are calling the function

Thanks for the response.

Function pointers and callbacks are not exactly my strong suit, so this is a situation where I have been trying to better my understanding of how to use them. Any suggested reading on the topic would be appreciated as well.

I guess the point where I was getting tripped up was that I was not realizing that the ino function: IncrementStoredVariable was returning that value back to the cpp: SetCallBackRight. In hindsight, that was probably my first mistake.

For some reason, I was thinking that—within the INO—I was plugging in a function to call within that one parameter. So I tried adjusting the C++ to do that, so that I would have a function within the arduino code to call, with its parameters, whenever the rest of my polling/resolution functions determined a clockwise or counterclockwise rotation.

So in the instance of:

Encoder1.SetCallbackRight(IncrementStoredVariable(Encoder1));

I was not thinking about the return of IncrementStoredVariable back to SetCallbackRight. So my logic was that I was just calling the INO Function and trying to pass the class instance, which the INO function would be agnostic to.

The goal was to try and keep the more complicated, mechanical logic in the C++ file while the ino could be streamlined for the interaction/HID logic which would be easier to go into and modify on the fly to finish up all the controls etc.

Realistically, from this answer and reviewing Qubits example, I should probably rethink the way that I am setting up the callback to begin with.

here is an example, hopefully easy to follow

the CB class is instantiated by passing a callback function pointer and a reference to an integer.
each time you call ping() on one instance, the referenced integer is incremented by 1 and the callback function is called.

Thank you all for your help!

I'm happy to report that I figured out what the problem was with my thought process after reviewing J-M-L's first post (marked solution) and Qubits-us's git repo that was helpful in reviewing a clean way of outlining and presenting everything.

The problem was, while I was getting my function from the .ino, I failed to realize that said function is actually being triggered in the polling function via the function pointer. For whatever reason, even after writing the darned thing, I was thinking it was still triggering in the ino. My execution order, mentally, was backwards.

Using typedef definitely helped clear everything up, even just to write it, and that's when I started realizing where I was tripping myself up.

Thanks again for the help. This has cleared up a subject in C++ that has always been hit or miss with me, and ultimately I just slogged through it.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.