Functional Pointer and Callback in C++

PrimerPy
6 min readFeb 27, 2023

Functional pointers and callbacks are powerful features of the C programming language that allow for dynamic function calls and can greatly enhance the flexibility of your code. In this post, we’ll take a closer look at what functional pointers and callbacks are, how they work, and provide some examples to help you understand how to use them in your own code.

Functional Pointer

A functional pointer is a pointer that points to a function rather than a data type. This means that you can assign a pointer to a function and then call that function using the pointer. This feature is particularly useful when you want to dynamically call different functions at runtime, based on some condition.

Here’s an example of how to use a functional pointer in C:

#include <stdio.h>

void add(int a, int b) {
printf("%d + %d = %d\\n", a, b, a + b);
}

void subtract(int a, int b) {
printf("%d - %d = %d\\n", a, b, a - b);
}

int main() {
void (*func_ptr)(int, int);
int a = 10, b = 5;
char op = '+';

if (op == '+') {
func_ptr = &add;
} else if (op == '-') {
func_ptr = &subtract;
}

func_ptr(a, b);

return 0;
}

In this example, we have two functions, add and subtract, that each take two integer arguments. We then declare a functional pointer, func_ptr, that takes two integer arguments and returns void. We use an if-else statement to assign func_ptr to either the add or subtract function, based on the value of the op variable. Finally, we call the function pointed to by func_ptr, passing in the values of a and b.

Callback Function

A callback function is a function that is passed as an argument to another function and is then called within that function. This allows you to write more flexible and reusable code, as you can pass in different functions to achieve different results. Callback functions are commonly used in event-driven programming, where you want to perform some action when a certain event occurs.

Here’s an example of how to use a callback function in C:

#include <stdio.h>

void print_hello() {
printf("Hello ");
}

void print_world() {
printf("world!\\n");
}

void call_functions(void (*func_ptr)()) {
(*func_ptr)();
}

int main() {
call_functions(&print_hello);
call_functions(&print_world);

return 0;
}

In this example, we have two functions, print_hello and print_world, that each print a message to the console. We then declare a function, call_functions, that takes a functional pointer as an argument and calls the function pointed to by that pointer. Finally, we call call_functions twice, passing in the pointers to the print_hello and print_world functions, respectively.

Comparison to Python Decorators

Functional pointers and callbacks in C share some similarities with decorators in Python. Both features allow for dynamic function calls and can enhance the flexibility of your code. However, while decorators in Python are a way to modify or extend the behavior of a function or class, functional pointers and callbacks in C are a way to pass functions as arguments to other functions or to dynamically call functions at runtime. Additionally, decorators in Python are a language feature, whereas functional pointers and callbacks in C are implemented using pointers and function pointers.

Practical Examples

Functional pointers and callbacks can be used in a wide variety of applications. Here are a few practical examples to help you understand how to use them in C++:

Sorting: You can use a functional pointer to pass a comparison function to a sorting algorithm, allowing you to sort arrays of any data type based on any criteria. For example, you may want to sort a list of names alphabetically, or sort a list of numbers in descending order.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
int id;
char* name;
} Employee;

int compare_by_id(const void* a, const void* b) {
Employee* e1 = (Employee*)a;
Employee* e2 = (Employee*)b;
return e1->id - e2->id;
}

int compare_by_name(const void* a, const void* b) {
Employee* e1 = (Employee*)a;
Employee* e2 = (Employee*)b;
return strcmp(e1->name, e2->name);
}

void sort_employees(Employee* employees, int n,
int (*compare)(const void*, const void*)) {
qsort(employees, n, sizeof(Employee), compare);
}

int main() {
Employee employees[] = {
{123, "John"},
{456, "Jane"},
{789, "Bob"}
};
int n = sizeof(employees) / sizeof(employees[0]);

// sort by id
sort_employees(employees, n, compare_by_id);
for (int i = 0; i < n; i++) {
printf("%d %s\\n", employees[i].id, employees[i].name);
}
printf("\\n");

// sort by name
sort_employees(employees, n, compare_by_name);
for (int i = 0; i < n; i++) {
printf("%d %s\\n", employees[i].id, employees[i].name);
}

return 0;
}

In this example, we have a struct Employee that contains an ID and name. We then write two comparison functions, compare_by_id and compare_by_name, that compare two Employee objects based on their ID and name, respectively. Finally, we write a function sort_employees that takes an array of Employee objects, the length of the array, and a comparison function, and sorts the array using the comparison function. We then call sort_employees twice, passing in the different comparison functions, to sort the array by ID and name.

Event-driven programming: You can use callbacks to handle events in your program, such as button clicks, keyboard presses, or network events. For example, you may want to perform a certain action when a user clicks a button, or update a network connection when new data is received.

#include <stdio.h>
#include <stdlib.h>

typedef void (*EventCallback)(void*);

typedef struct {
void* data;
EventCallback callback;
} Event;

void process_event(Event event) {
event.callback(event.data);
}

void handle_mouse_click(void* data) {
int* coords = (int*)data;
printf("Mouse clicked at (%d, %d)\\n", coords[0], coords[1]);
}

void handle_keyboard_press(void* data) {
int key = *(int*)data;
printf("Keyboard key pressed: %d\\n", key);
}

int main() {
int mouse_coords[] = {10, 20};
int key = 27;

Event mouse_event = {mouse_coords, handle_mouse_click};
process_event(mouse_event);

Event keyboard_event = {&key, handle_keyboard_press};
process_event(keyboard_event);

return 0;
}

In this example, we define a struct Event that contains a pointer to some data and a callback function that takes that data as an argument. We then define two callback functions, handle_mouse_click and handle_keyboard_press, that print messages to the console based on the data passed to them. Finally, we define a function process_event that takes an Event object and calls the callback function with the data in the Event object. We create two Event objects, one for a mouse click and one for a keyboard press, and call process_event on each one.

Multithreading: You can use functional pointers to pass functions to threads, allowing you to perform multiple tasks simultaneously. For example, you may want to process data in the background while the user continues to interact with the program.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

typedef void (*ThreadFunction)(void*);

void* thread_function(void* data) {
ThreadFunction function = (ThreadFunction)data;
function(NULL);
return NULL;
}

void create_thread(ThreadFunction function) {
pthread_t thread_id;
pthread_create(&thread_id, NULL, thread_function, function);
}

void print_hello() {
printf("Hello ");
}

void print_world() {
printf("world!\\n");
}

int main() {
create_thread(&print_hello);
create_thread(&print_world);

pthread_exit(NULL);
}

In this example, we define a function thread_function that takes a ThreadFunction as an argument and calls it with a NULL argument. We then define a function create_thread that creates a new thread and calls thread_function with the specified ThreadFunction. We define two ThreadFunctions, print_hello and print_world, that print messages to the console. Finally, we create two threads, one for each ThreadFunction, and wait for them to finish.

Conclusion

In conclusion, functional pointers and callbacks are powerful features of the C programming language that can greatly enhance the flexibility and usability of your code. By using functional pointers, you can dynamically call different functions at runtime, while callbacks allow you to pass functions as arguments to other functions, making your code more modular and flexible. We’ve seen practical examples of how these features can be used in sorting, event-driven programming, and multithreading. Although there may be similarities with other programming languages, functional pointers and callbacks in C provide a unique way to enhance your code. By mastering these concepts, you can take your C programming skills to the next level and create more dynamic and flexible programs.

--

--

PrimerPy

www.primerpy.com | A Primer on Python. I'm a veteran Data and Web developer based out of NYC and DC area. I blog mainly on Python and other tech related topics.