2007年7月8日星期日

Callback (computer science)

Callback (computer science) - Wikipedia, the free encyclopedia

Callback (computer science)

A callback is often back on the level of the original caller.
A callback is often back on the level of the original caller.
In another common scenario, the callback is first registered and later called asynchronously.
In another common scenario, the callback is first registered and later called asynchronously.

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.

Motivation

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)

Example

The following code in C demonstrates the use of callbacks for the specific case of searching an array for an item (namely, 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) {
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 like C or C++ (in C++ and OO languages other solutions are however possible, see below). Dynamically scoped languages (including functional programming languages) can provide access to application data automatically via closures. In Lisp, for example, the same program would look like this:

; LIBRARY CODE
(defun traverseWith (array callback)
(let ((exitCode nil)
(i 0))
(while (and (not exitCode) (< i (length array)))
(setq exitCode (callback i (aref array i)))
(setq i (+ i 1)))
exitCode))

; APPLICATION CODE
(let (index found)
(setq found (traverseWith array (lambda (idx item)
(if (<= item 5) nil
(setq 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.

Implementation

The form of a callback varies among programming languages .

  • C and C++ allow function pointers as arguments to other functions.
  • Several programming languages (though especially functional programming languages such as Scheme or ML) allow closures, a generalization of function pointers, as arguments to other functions.
  • Several programming languages, especially interpreted languages , allow one to pass the name of a function A as a parameter to a function B and have B call A by means of eval.
  • In object-oriented programming languages , a call can accept an object that implements some abstract interface, without specifying in detail how the object should do so. The programmer who implements that object may use the interface's methods exclusively for application-specific code. Such objects are effectively a bundle of callbacks, plus the data they need to manipulate. They are useful in implementing various design patterns like Visitor, Observer, and Strategy.
  • C++ allows objects to provide their own implementation of the function call operation. The Standard Template Library accepts these objects (called functors), as well as function pointers, as parameters to various polymorphic algorithms
  • Some systems have built-in programming languages to support extension and adaptation. These languages provide callbacks without the need for separate software development tools.

Special cases

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 occur). Some programming languages also have direct support for this construct (for instance .NET delegates or Qt's signals and slots).

See also

External links


没有评论: