Question on function pointers and callbacks - Page 1
Forum section: electronics / general

author: unscripted
author_rank: Contributor
timestamp: February 20, 2023, 02:16:14 am
content: Rather than callbacks itself, this may be more a C programming-related question. Going through some callbacks examples I'm stuck in understanding this one:In an object file I see these declarations:Code://Prototype for the function in the application programvoid callback_execution(void(*fn)(void));//initialize fp pointer to point to callback_execution( ))void (*fp)(void(*fn)(void)) = &callback_execution;//Function to toggle some pinsvoid callback_function(void){//toggle some pins}//Timer interrupt handlervoid timer_handler(){//Check for timer interrupt and clear it. Then call the function pointed to by fp// callback_execution( ) is pointed to by fp and// resides in the main application program.fp(&callback_function);}So let me see if I understand how this works:Here I'm registering the callback_execution function.Code:void (*fp)(void(*fn)(void)) = &callback_execution;Then in the interrupt handlerfpis called that points tocallback_executionAND it is passed the address ofcallback_functionas parameter.Code:fp(&callback_function);Here it's where I'm confused.fpwas initialized to point tocallback_execution. But I'm passingcallback_functionaddress as the argument of fp. Does this meancallback_executionwill be called and passedcallback_functionas its argument? The function prototypevoid callback_execution(void(*fn)(void));is only defined in the main application but I don't see it is being explicitly called. So I'm assuming is being implicitly called by fp. Correct me where I'm wrong.

author: cv007
author_rank: Frequent Contributor
timestamp: February 20, 2023, 02:51:16 am
content: You can test in the online compiler-https://godbolt.org/z/4cbcbcedrThe callback_execution function needs to be somewhere, as the declaration just informs the compiler it will be somewhere.The fp is a function pointer that when used takes a function pointer as an argument. When fp is used via (), it will need an argument of the type it was declared with- void(*)(), which in this case is the callback_function function. The functions do not need & as that is implied for functions.It seems like quite a route to take to get to your function (timer_handler to callback_execution via fp, to callback_function inside callback_execution), but they may have reasons to do this even if they are not good reasons (maybe the callback_execution is doing logging or other things in addition to calling your function). It can be quite confusing when they decide to create these layers.

author: unscripted
author_rank: Contributor
timestamp: February 20, 2023, 03:10:19 am
content: Quote from: cv007 on February 20, 2023, 02:51:16 amThank you. That route is what it's still confusing to me. I get your point. The confusion for me is because fp(&callback_function) doesn't show callback_execution explicitly. So I thought it would call callback_function directly. If I were to read the function I'd say that callback_execution is called by fp implicitly in the expression fp(&callback_function). And callback_execution is passed callback_function as the argument.

author: slugrustle
author_rank: Frequent Contributor
timestamp: February 20, 2023, 03:53:12 am
content: That example is also really odd to me because I don't think there's a point to using callbacks unless you're passing around the pointer to the callback function dynamically at runtime.

author: pcprogrammer
author_rank: Super Contributor
timestamp: February 20, 2023, 06:49:53 am
content: Callbacks are often used in hardware abstraction layers and use static pointers or fixed function names to make it work, but the given example is indeed weird.In this case the function has to be named "callback_execution" in the application part and has to have "void(*fn)(void)" as input parameter. They then use a global function pointer to call it in the interrupt handler.The only advantage I see for it, is the ability for the application programmer to use the global function pointer "fp" to set a different callback function at run time.The fact that it passes the execution address of the function allows some sort of verification as to which function is being used?Yep definitely weird.

author: Berni
author_rank: Super Contributor
timestamp: February 20, 2023, 07:10:04 am
content: Yeah this does not make any sense for passing itself as a parameter.Maybe the function would do something else if you pass it a different pointer, like making a chain of calls or something.Normally callback functions would have a pointer to some sort of context structure instead. Like if you have 1 function servicing multiple timer interrupts you would pass in the timers address or perhaps some structure that has the timer address as part of it. Tho this is basically just implementing the functionality of C++ classes using regular C features.

author: paulca
author_rank: Super Contributor
timestamp: February 20, 2023, 10:35:30 am
content: This looks like a use of dynamic function pointer callbacks such as used in Closure or Javascript. Everything is a function that takes a function and returns a function which takes a function. Apparently some peoples brains actually work that way, but no, really it's easier to implement in ASM.I have used function pointer arguments, for chaining call backs to create "do this", then "do this", unless this, then "do this" interrupt chains.I can see how assigning those function pointers (static pointers) in my case to a variable might be useful.These techniques also for a lot of the basis of C++ OOP under the hood and reoccurs a lot generally in OOP for everything from inheritance, to polymorphism.del = object->getDeligate( candidate );results = del->process( candidate );ui->getRenderer( results )->render();Would basically be a whole set of dynamic struct and dynamic function pointers in C.

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 20, 2023, 12:40:58 pm
content: Yup, callingfp(&callback_function)is quite odd; one would expect it to befp(), i.e. passing no parameters, because of the function pointer type.There are three very commonly used patterns, if it matters:Passing a function pointer to a function be applied for array members. Seeqsort()standard C library function, which takes a pointer to a function used to compare array elements.Passing the pointer of a structure where the function pointer itself resides, roughly similarly to OOPthisorselfkeywords.An example is generic list/array/hash table support, where the structure is an instance of the data store, and contains function pointer(s) for specific operations. By implementing the operation functions for different types of data elements, user can use the same interface for all datastores of that type, regardless of which of the supported element data types it uses.Passing a private pointer to the function, stored in the same structure.An example is a driver structure, or an instance of for example human interface button handler in a microcontroller firmware, where the private pointer contains the instance-specific information. The main structure can then support different types of elements, each type having their own handler functions, and taking a pointer to the instance-specific private information, which is opaque from outside.