I am struggling with callback functions in C.
Basically I am trying to write some middleware for a proof of concept. I am using a 32-bit zero gecko from Silabs, with an UWB radio module from Decawave.
I am attempting to develop this code with modularity in mind. I want to decouple it from both the host system and radio of choice. Another project I want to use this for is a wifi module and I won't be using the zero gecko even past the POC with the UWB module.
Either way, I am trying to decouple the HAL from the middleware and then call in any desired combination of the two HALs.
The gpio function is necessary because asserting desired lines on power-up sets the spi polarity and alpha, as do other wireless modules.
Middleware layer:
void radio_Init(void (*radio_spi_polalpha)()){
radio_spi_polalpha();
}
UWB HAL:
/* callback for radio_Init()*/
void dw_SpiSetPolAlpha(void (*gpio_setfn)()){
//call to GPIO lower-level fn
gpio_setfn();
}
Host HAL:
/* callback for dw_SpiSetPolAlpha()*/
void zg_SetDwSpiPolAlpha(const int mode){
switch (mode){
case 0: gpio->P[PORTC].DOUTSET |= DW_SPI_LPOL_LPHA_MASK;
break;
case 1: gpio->P[PORTC].DOUTSET |= DW_SPI_LPOL_HPHA_MASK;
break;
case 2: gpio->P[PORTC].DOUTSET |= DW_SPI_HPOL_LPHA_MASK;
break;
case 3: gpio->P[PORTC].DOUTSET |= DW_SPI_HPOL_HPHA_MASK;
break;
}
}
Application layer call:
radio_Init(dw_SpiSetPolAlpha(zg_SetDwSpiPolAlpha(DW_SPI_LPOL_LPHA_MASK)));
But all I get is "invalid use of void expression"
I don't understand though, because all of my functions are void return types.
I fooled around for a bit and tried this:
UWB HAL:
/* callback for radio_Init()*/
void dw_SpiSetPolAlpha(void (*gpio_setfn)(int)){ <-- add int as parameter
//call to GPIO lower-level fn
gpio_setfn();
}
but then I get "too few arguments to gpio_setfn", which I then tried to pass in the argument to gpio_setfn() like so:
gpio_setfn(int);
but I just get "expected expression before int"
and I tried:
/* callback for radio_Init()*/
void dw_SpiSetPolAlpha(void (*gpio_setfn)(int mode)){ <-- add int as parameter
gpio_setfn(mode);
}
but I just get an implicit declaration error
I apologise if this is a duplicate, but I have done my best to look through as many threads on this issue as I can, but either none seem to answer my question or I have missed some vital clue. Researched threads are as follows:
Error invalid use of void expression
Error: invalid use of void expression
"Error: invalid use of void expression" when using function as parameter of another[duplicate]
How do you pass a function as a parameter in C
I understand the concept behind callback functions, hence why I want to use them to achieve decoupling. If someone could provide some clarification as to where I have gone wrong I would be very grateful.
Thanks
[FINAL EDIT WITH SOLUTION]: completed update:
First of all, thank you guys once again for being so helpful. (I will end up typedef'ing this out in refactor, having to constantly redefine the type got annoying)
Here is the completed code which works as expected.
host HAL:
void zg_SetDwSpiPolAlpha(const int* mode){
switch (*mode){
case 0: gpio->P[PORTC].DOUTSET |= DW_SPI_LPOL_LPHA_MASK;
break;
case 1: gpio->P[PORTC].DOUTSET |= DW_SPI_LPOL_HPHA_MASK;
break;
case 2: gpio->P[PORTC].DOUTSET |= DW_SPI_HPOL_LPHA_MASK;
break;
case 3: gpio->P[PORTC].DOUTSET |= DW_SPI_HPOL_HPHA_MASK;
break;
}
}
radio HAL:
void dw_SpiSetPolAlpha(void (*gpio_setfn)(const int*), const int* mode){
//call to GPIO lower-level fn
gpio_setfn(mode);
}
Middleware:
void radio_Init(void (*radio_spi_polalpha)(void (*)(const int*), const
int*), void (*device_gpio_set)(const int*), const int* mode)
{
radio_spi_polalpha(device_gpio_set, mode);
}
Application call:
const int one = 1;
radio_Init(dw_SpiSetPolAlpha, zg_SetDwSpiPolAlpha, &one);
A few things to note for anyone else who stumbles on this problem:
-watch out for f-pointer declarations, especially when as parameters: this one threw me a little because of the enclosing brackets around the f-pointer label.
Function with parameter:
<return_type> <label>(<f-pointer return_type> (* <label>)( <parameter> )) { } //notice the closing parentheses BEFORE the opening for parameter section. Also note that <label> is optional when declaring a function pointer as a parameter to a function pointer
NOT!:
<return_type><label>(<f-pointer return_type> * <label>(<parameter>)) //similar to expected normal syntax for a function declaration
-passing in a f-pointer as a parameter to an f-pointer, requires two things:
- declare the f-pointer as a parameter within that first f-pointer declaration
- the actual function you pass in must also declare a f-pointer as a parameter
-when you pass in f-pointers, do not call them as you pass them. Call them when you need to call them, neither in the parameter declaration nor the call.
-Also when passing in multiple parameters for calls that have multiple levels, remember that it works like a funnel: you pass in any parameters to function pointers as a separate parameter of the parent call. (hence the redeclaration of the 'const int* mode', which then get's passed into a function expecting a const int*). This applies to multiple levels of function pointers as parameters to other function pointers.
These things are nothing more than syntax errors, but just thought I'd share some simple gotchas to look out for when you are declaring and calling this wonderfully useful, yet initially confusing language feature.
Peace.