1

I'm looking through a someone else's code base and I see this function prototype:

void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );

And it's defined as follows:

void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
    Radio.Sleep( );
    BufferSize = size;
    memcpy( Buffer, payload, BufferSize );
    RssiValue = rssi;
    SnrValue = snr;
    State = RX;
}

But, it's called later on in the super-loop of main as shown here:

RadioEvents.RxDone = OnRxDone;

How on earth is that legal? That function is expecting a character pointer, and three ints, but yet it get's nothing and this works fine. What is going on here?

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
testname123
  • 1,061
  • 3
  • 20
  • 43
  • Updated the question. – testname123 Dec 10 '16 at 06:19
  • Rolled back. Once you got an answer you should not change the question! We are no discussion forum. If you have a new question, open a new question! But best would be to get those basics from a good C book. Particular questions or following unstructured tutorials or youtube videos will not give the whole picture as necessary. – too honest for this site Dec 11 '16 at 21:28

3 Answers3

7

RadioEvents.RxDone = OnRxDone;

This is not calling function OnRxDone. Instead it is assigning the pointer to function OnRxDone to another function pointer which is a member of RadioEvents. Here the function name is used as a pointer to that function. Later it can be called by using function pointer RadioEvents.RxDone with required parameters.

Edit: after the edit in the question it seems that OP is asking about the details of function pointer. Googling "c function pointer" returns this question as first result: How do function pointers in C work?

Community
  • 1
  • 1
taskinoor
  • 45,586
  • 12
  • 116
  • 142
2

RadioEvents.RxDone = OnRxDone;

It appears RadioEvents is a struct of function pointers, and RxDone is one of them, and so the assignment is valid.


After the question edit:

As pointed out, the answer lies in the struct of RadioEvents, but this only clarifies this if you understand what a struct of function pointers does. Can someone elaborate on how this all ties together and what the whole story is with this?

The story behind this is that struct RadioEvents defines an interface. For example, the Linux kernel defines such interface such that all wireless driver needs to follow. The kernel does not need to know the implementation details of each driver, all it needs is that each driver must implement those functions defined in that struct RadioEvents.

Each driver has a different implementation of function RxDone - the implementation details can be hardware specific. But as long as the driver implements that prototype, it can be plugged in the kernel.

You can observe the exact match between the function API RxDone in RadioEvents:

void ( *RxDone )( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );

and that of OnRxDone:

void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );

This is sometimes referred to as polymorphism in the kernel.

artm
  • 17,291
  • 6
  • 38
  • 54
2

RadioEvents.RxDone is a variable of function-pointer type. It can refer to any function with a specific parameter and return type signature defined by its type definition. In this case the structure member RxDone is defined thus:

void ( *RxDone )( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );

Indicating that it refers to a function returning void and taking four arguments of the types uint8_t*, uint16_t, int16_t, and int8_t respectively. The parameter names in this declaration are optional and it could equelly be:

void (*RxDone)( uint8_t*, uint16_t, int16_t, int8_t );

or even

tRxDone RxDone ;

where tRxDone is defines thus:

typedef (*tRxDone)( uint8_t*, uint16_t, int16_t, int8_t );

The function assigned to RadioEvents.RxDone may be invoked subsequently (not shown in your code fragment) thus:

RadioEvents.RxDone( data, data_length, rssi, snr ) ;

The purpose of using a pointer to a function is that it provided support for late binding where the function to be invoked is determined at run-time rather than hard coded by the linker and build-time. It is often used for callbacks where some library code will call a user supplied function in order to adapt and customise the behaviour. In turn callbacks are often used for event handlers which appears to be what is going n in your example.

What the assignment of RadioEvents.RxDone does is essentially tell the library *"when event Rx Complete happens call my function OnRxDone"*.

Clifford
  • 88,407
  • 13
  • 85
  • 165