2

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:

  1. declare the f-pointer as a parameter within that first f-pointer declaration
  2. 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.

BitShift
  • 671
  • 5
  • 21
  • You can find a nice way to handle this here: https://techtalk.intersec.com/2014/11/blocks-rewriting-with-clang/ – Ôrel Oct 05 '18 at 12:25
  • I recommend using a `typedef` for your callbacks, for example: `typedef void (*callback)(void);`. – Fiddling Bits Oct 05 '18 at 12:28
  • I will definitely look into both of these solutions... that blocks thing looks cool. Definitely want to get this raw concept in my head though before I start abstracting any further. My brain is currently turning to mush. – BitShift Oct 05 '18 at 12:41

1 Answers1

1

You have some issues where you're trying to pass function pointers around. It looks like you're passing the result of a function call instead of passing the function itself.

Take for example this line: void dw_SpiSetPolAlpha(void (*gpio_setfn)())

This is declaring a function dw_SpiSetPolAlpha that takes as a parameter a pointer to a function. The pointer can be referenced as gpio_setfn and it must point to a function that returns void and takes no parameters.

But at the point you are calling dw_SpiSetPolAlpha, you are passing zg_SetDwSpiPolAlpha(DW_SPI_LPOL_LPHA_MASK), which is a function invocation -- so you're passing void, the result -- not the function itself.

If instead you declare void dw_SpiSetPolAlpha(void (*gpio_setfn)(int)) then you would be able to pass the function pointer using dw_SpiSetPolAlpha(zg_SetDwSpiPolAlpha) and within dw_SpiSetPolAlpha you could utilize the function pointer -- and actually call the function -- by calling gpio_setfn(DW_SPI_LPOL_LPHA_MASK)


Editing, using some typedefs. (I'm not doing this in front of a compiler, this is off the top of my head, so some tweaking may be necessary...)

typedef void (*callback)(int);
typedef void (*callback_setter)(callback);

void dw_SpiSetPolAlpha(callback gpio_setfn) {
...
}

void radio_Init(callback_setter radio_spi_polalpha)) {
...
}
jwismar
  • 11,772
  • 3
  • 28
  • 42
  • I see... I have just tried to do that and still have the same error. I will update in my OP. – BitShift Oct 05 '18 at 12:35
  • I was giving you an example, not solving all of the problems. You have the same issue with your call to `radio_Init`, which is expecting a pointer to a function that has no parameters. But you're passing to it a function invocation, from a function that does take a parameter.... When you get to the point where you're passing a pointer to a function that takes a pointer to a function, you're definitely getting into territory where some `typedef`s are going to help you. – jwismar Oct 05 '18 at 12:43
  • haha sorry that wasn't supposed to be a snarky comment... ok yeh I just realised what I'm doing in terms of the parameters here. Ok cool, thank you so much, I can't believe I didn't see this. Evidently time for bed. – BitShift Oct 05 '18 at 12:48