The meaning of a callback function is: a function that is not called by the programmer anywhere in their code, but called from an external source. Typically, this means the OS.
- If the external source calling the function is hardware, the called function is named "interrupt service routine".
- If the external source is software, the called function is named "callback function."
For example, you will find plenty of callback functions in Windows programming. Every graphical window has a callback function "WindowProc" that receives a call from the OS for every action occurring on the window. It is then up to the application programmer to write the callback function and handle the events they are interested in. Events that they aren't interested in is passed to the default handler (or base class, if you will).
If the programmer wants something special to happen when the mouse is clicked on a window, they can overwrite the default behavior of a mouse click, by replacing the default behavior with their own code. This way you achieve polymorphism, even in C programs.
Example from Windows programming:
LRESULT CALLBACK WindowProc (HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch(umsg)
{
case WM_LBUTTONDOWN:
do_something(); /* overwrite default behavior of a left mouse button click */
break; /* break = and then execute the default behavior afterwards */
case WM_RBUTTONDOWN:
do_something(); /* overwrite default behavior of a right mouse button click */
return 0; /* overwrite the default behavior entirely */
}
// execute default behavior for the event:
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
The break
from the switch statement is similar to plain inheritage: execute the inherited function and then the base class function. While the return 0;
is similar to polymorphism/virtual inheritage, since the inherited object overwrites default behavior entirely.
A sidenote: Since a callback function is called from an external source and not the program itself, some of the dumber compilers will make assumptions that the function is never called and therefore perform dangerous optimizations.
For example, had the global variable "flag" been modified from the WindowProc above, and code in main()
relies on "flag", the compiler would possibly optimize away the code in main()
, since it believes that "flag" was never used anywhere.
To avoid this incorrect compiler behaviour, it is good practice to always declare all variables shared between a callback function and the rest of the program as volatile
, which will block all optimizations of that variable.