C Basics: Pass State to a Callback Without Global Variables
A reusable traversal should not decide where callers keep their totals. Pass a callback and its state explicitly instead of depending on a global variable.
Prepare a scratch directory
Tested on Linux amd64 with GCC 14.2.0. You need GCC with C17 support, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. No external library is required.
Keep the same terminal open. Stop if setup fails, before saving the source:
start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1Run the example
Save this as callback-context-demo.c:
#include <stddef.h>
#include <stdio.h>
typedef void (*value_callback)(int value, void *context);
struct summary { int total; size_t count; };
static void visit_values(const int *values, size_t count,
value_callback callback, void *context) {
for (size_t index = 0; index < count; ++index) {
callback(values[index], context);
}
}
static void collect_value(int value, void *context) {
struct summary *summary = context;
summary->total += value;
++summary->count;
}
int main(void) {
const int values[] = { 3, 5, 7 };
struct summary result = { .total = 0, .count = 0 };
visit_values(values, sizeof values / sizeof values[0],
collect_value, &result);
printf("Count: %zu\n", result.count);
printf("Total: %d\n", result.total);
return 0;
}Compile and run it. The program runs only if compilation succeeds:
gcc -std=c17 -Wall -Wextra -Werror -pedantic callback-context-demo.c -o callback-context-demo &&
./callback-context-demoObserved output:
Count: 3
Total: 15Read the result
The traversal invokes a compatible function pointer. The callback converts the context back to the object type supplied by the caller. State stays local to main.
Apply it carefully
The callback and context must agree on the pointed-to type, and the array, callback and context must remain valid throughout the visit. This function calls synchronously and retains nothing. An asynchronous API needs an explicit lifetime contract. The example values fit in int; production totals need overflow handling.
See the WG14 C draft, sections 6.3.2.3 and 6.5.2.2 for the underlying language rules.
Clean up
Remove only the source and executable created here, return to the starting directory, and remove the empty practice directory:
rm -- callback-context-demo.c callback-context-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directoryIf the directory is not empty, inspect its contents before removing anything else.
Acknowledgements
This post was created with assistance from AI.