
In computer programming, a callback is executable code that is passed as an argument to other code. It allows a lower-level software layer to call a subroutine (or function) defined in a higher-level layer.
Usually, the higher-level code starts by calling a function within the lower-level code, passing to it a pointer or handle to another function. While the lower-level function executes, it may call the passed-in function any number of times to perform some subtask. In another scenario, the lower-level function registers the passed-in function as a handler that is to be called asynchronously by the lower-level at a later time in reaction to something.
A callback can be used as a simpler alternative to polymorphism and generic programming, in that the exact behavior of a function can be dynamically determined by passing different (yet compatible) function pointers or handles to the lower-level function. This can be a very powerful technique for code reuse.
Contents |
To understand the motivation for using callbacks, consider the problem of performing an arbitrary operation on each item in a list. One approach is to iterate over the list, operating on each object. This is the most common solution in practice, but it is not ideal; the code to manage the iterator (for example, a for statement) must be duplicated at each point in the code where the list is traversed. Furthermore, if the list is updated by an asynchronous process (for example, if an item is added or removed), the iterator might skip over items or become corrupt during the traversal.
An alternative might be to create a new library function that performs the desired operation with appropriate synchronization. This approach still requires each new library function to contain the code to traverse the list. This solution is not acceptable for generic libraries intended for various applications; the library developer cannot anticipate every application need, and the application developer should not need to know the details of the library implementation.
Callbacks solve these shortcomings. One procedure is written to traverse the list, and this procedure uses application-provided code to operate on each item. There is a clear distinction between the library and the application without sacrificing flexibility.
A callback may also be regarded as a form of runtime binding, as discussed in the influential Design Patterns book (see the Chapter 1 summary).
The following code in C demonstrates the use of callbacks for the specific case of searching an array for an item (in this case, the first integer greater than 5). First, the iteration approach:
int i; for (i = 0; i < length; i++) { if (array[i] > 5) { break; } } if (i < length) { printf("Item %d\n", i); } else { printf("Not found\n"); }
Next, the callback approach:
/* LIBRARY CODE */ int traverseWith(int array[], size_t length, int (*callback)(int index, int item, void *param), void *param) { int exitCode = 0; for (int i = 0; i < length; i++) { exitCode = callback(i, array[i], param); if (exitCode != 0) { break; } } return exitCode; } /* APPLICATION CODE */ int search (int index, int item, void *param) { if (item > 5) { *(int *)param = index; return 1; } else { return 0; } } /* (in another function) */ int index; int found; found = traverseWith(array, length, &search, &index); if (found) { printf("Item %d\n", index); } else { printf("Not found\n"); }
Note that traverseWith receives an extra parameter that the callback can use for its own purposes. Normally a callback uses such a parameter as a pointer to application data outside its own scope (in this case, the variable that receives the index). This feature is necessary only in a statically scoped language such as C or C++ (in C++ and OO languages other solutions are however possible, see below). Lexically scoped languages supporting closures (including functional programming languages) can provide access to application data automatically by callbacks referring to local variables. In Lisp, for example, the same program would look like this:
; LIBRARY CODE (defun traverse-with (array callback) (let ((exit-code nil) (i 0)) (loop while (and (not exit-code) (< i (length array))) do (progn (setf exit-code (funcall callback i (aref array i))) (setf i (+ i 1)))) exit-code)) ; APPLICATION CODE (let (index found) (setf found (traverse-with array #'(lambda (idx item) (unless (<= item 5) (setf index idx) t)))))
The callback function is now defined at the point of use, and it refers to "index" by name. Synchronization concerns have been omitted from these examples, but they can easily be addressed in the traverseWith function. More importantly, they can be addressed, or ignored, by changing only that function.
The form of a callback varies among programming languages.
Callback functions are also frequently used as a means to handle exceptions arising within the low level function, as a way to enable side-effects in response to some condition, or as a way to gather operational statistics in the course of a larger computation. Interrupt handlers in an operating system respond to hardware conditions, signal handlers of a process are triggered by the operating system, and event handlers process the asynchronous input a program receives.
A pure callback function is one which is purely functional (always returns the same value given the same inputs) and free of observable side-effects. Some uses of callbacks, such as the sorting example, require pure callback functions to operate correctly.
A special case of a callback is called a predicate callback, or just predicate for short. This is a pure callback function which accepts a single input value and returns a Boolean value. These types of callbacks are useful for filtering collections of values by some condition.
In event-driven programming, there is often use, in different forms, of the Observer pattern, which essentially allows the use of multicast callbacks, and where callbacks are registered early, to be invoked at callee's discretion (i.e. when a given event occurs). Some programming languages also have direct support for this construct (for instance .NET delegates or Qt's signals and slots).
Why are we here?
All text is available under the terms of the GNU Free Documentation License
This page is cache of Wikipedia. History