Introduction
For many beginners, they often think that callback functions are mysterious and want to know How callback functions work. This article will explain what callback functions are, what benefits they have, and why they are used. Before you start, let's assume that you have learned about function pointers.
What is a callback function?
In short, a callback function is a function called through a function pointer. If you pass the pointer (address) of a function as a parameter to another function, when this pointer is used to call the function to which it points, we will say this is a callback function.
Why should I use a callback function?
Because the caller and the called can be separated. The caller does not care who is called. All they need to know is that there is a called function with a specific prototype and certain restrictions (for example, the returned value is int.
If you want to know what the callback function has in practice, first assume that there is such a situation, we need to write a library, which provides some sortingAlgorithmSuch as Bubble sorting, quick sorting, shell sorting, and shake sorting. However, to make the database more generic, you do not want to embed the sorting logic in the function, let the user implement the corresponding logic; or, if you want the database to be used for multiple data types (INT, float, string), what should you do? Function pointers can be used for callback.
Callback can be used in the notification mechanism, for exampleProgramWhen a timer is set, the program will be notified at a certain time, but the implementers of the notification mechanism do not know anything about our program. At this time, we need a function pointer of a specific prototype, which is used for callback to notify us that the program event has occurred. In fact, the settimer () API uses a callback function to notify the timer. In case a callback function is not provided, it sends a message to the Message Queue of the program.
Another API function using the callback mechanism is enumwindow (), which enumerates all the top-level windows on the screen, calls a function provided by a program for each window, and transmits the processing program of the window. If the caller returns a value, iteration continues. Otherwise, exit. Enumwindow () does not care about where the caller is or what the handler passed by the caller is. It only cares about the returned value because it will continue to execute or exit based on the returned value.
In any case, the callback function continues from the C language. Therefore, in C ++CodeThe callback function is used only when an interface is created or when dealing with an existing callback interface. In addition to the above cases, the virtual method or function operator should be used in C ++, rather than the callback function.
A simple callback function implementation
A sort. DLL dynamic link library, which exports a type named comparefunction -- typedef int (_ stdcall * comparefunction) (const byte *, const byte *), it is the type of the callback function. In addition, it exports two methods: bubblesort () and quicksort (). The two methods have the same prototype but implement different sorting algorithms.
Void dlldir _ stdcall bubblesort (byte * array, int size, int elem_size, comparefunction cmpfunc );
Void dlldir _ stdcall quicksort (byte * array, int size, int elem_size, comparefunction cmpfunc );
These two functions accept the following parameters:
· Byte * array: pointer to the element array (of any type ).
· Int size: number of elements in the array.
· Int elem_size: the size of an element in the array, in bytes.
· Comparefunction cmpfunc: a pointer to the callback function with the above prototype.
The two functions sort the array in a certain way, but each time they determine which of the two elements is at the top, and the function has a callback function whose address is passed as a parameter. For developers, they don't have to worry about where the function is implemented or how it is implemented. They only need to care about the addresses of two elements used for comparison, and return the following value (both the database writer and user must comply with this Convention ):
·-1: If the first element is small, it should be placed before the second element in the sorted array.
· 0: if the two elements are equal, their relative positions are not important. In the sorted array, no one is above.
· 1: If the first element is large, it should be placed behind the second element in the sorted array.
Based on the above conventions, the implementation of the function bubblesort () is as follows, and quicksort () is a little more complex:
Void dlldir _ stdcall bubblesort (byte * array, int size, int elem_size, comparefunction cmpfunc)
{
For (INT I = 0; I <size; I ++)
{
For (Int J = 0; j <size-1; j ++)
{
// Callback comparison Function
If (1 = (* cmpfunc) (array + J * elem_size, array + (J + 1) * elem_size ))
{
// Two elements are exchanged.
Byte * temp = new byte [elem_size];
Memcpy (temp, array + J * elem_size, elem_size );
Memcpy (array + J * elem_size, array + (J + 1) * elem_size, elem_size );
Memcpy (array + (J + 1) * elem_size, temp, elem_size );
Delete [] temp;
}
}
}
}
Note: Because memcpy () is used in the implementation, the data type used by the function is limited.
The user must have a callback function whose address must be passed to the bubblesort () function. Here are two simple examples: one compares two integers and the other compares two strings:
Int _ stdcall compareints (const byte * velem1, const byte * velem2)
{
Int elem1 = * (int *) velem1;
Int elem2 = * (int *) velem2;
If (elem1 <elem2)
Return-1;
If (elem1> elem2)
Return 1;
Return 0;
}
Int _ stdcall comparestrings (const byte * velem1, const byte * velem2)
{
Const char * elem1 = (char *) velem1;
Const char * elem2 = (char *) velem2;
Return strcmp (elem1, elem2 );
}
Another program is used to test all the above Code. It passes an array with five elements to bubblesort () and quicksort (), A pointer to the callback function is also passed.
Int main (INT argc, char * argv [])
{
Int I;
Int array [] = {5432,432 1, 3210,210 9, 1098 };
Cout <"Before sorting ints with bubblesort \ n ";
For (I = 0; I <5; I ++)
Cout <array [I] <'\ n ';
Bubblesort (byte *) array, 5, sizeof (array [0]), & compareints );
Cout <"after the sorting \ n ";
For (I = 0; I <5; I ++)
Cout <array [I] <'\ n ';
Const char STR [5] [10] = {"Estella", "danielle", "crisy", "Bo", "Angie "};
Cout <"Before sorting strings with quicksort \ n ";
For (I = 0; I <5; I ++)
Cout <STR [I] <'\ n ';
Quicksort (byte *) STR, 5, 10, & comparestrings );
Cout <"after the sorting \ n ";
For (I = 0; I <5; I ++)
Cout <STR [I] <'\ n ';
Return 0;
}
If you want to sort the data in descending order (the big element comes first), you only need to modify the code of the callback function or use another callback function. This makes programming more flexible.
Call conventions
In the above code, you can find _ stdcall in the function prototype. Because it is prefixed with double underscores, It is a compiler-specific extension. In the end, it is Microsoft's implementation. Any program supporting Win32 development must support this extension or its equivalents. The standard call Convention is used for functions marked with _ stdcall. Why is it a standard convention, because all Win32 APIs (except for some accepted variable parameters) use it. Standard call conventions remove parameters from the stack before they are returned to the caller. This is also Pascal's standard convention. But in C/C ++, the call Convention is that the caller is responsible for clearing the stack, rather than being called the function. To force the function to use the C/C ++ call convention, you can use _ cdecl. In addition, variable parameter functions also use the C/C ++ call convention.
The Windows operating system adopts the standard call Convention (Pascal Convention) because it can reduce the size of the Code. This was important for early windows because it was running on a computer with only KB of memory.
If you do not like _ stdcall, you can also use the callback macro, which is defined in windef. h:
# Define callback _ stdcallor
# Define callback Pascal // and Pascal is # defined into _ stdcall
C ++ method as a callback function
Because c ++ is usually used to write code, you may want to write the callback function as a method in the class. But let's take a look at the following code:
Class ccallbacktester
{
Public:
Int callback compareints (const byte * velem1, const byte * velem2 );
};
Bubblesort (byte *) array, 5, sizeof (array [0]),
& Ccallbacktester: compareints );
If Microsoft compiler is used, the following compilation error is returned:
Error c2664: 'bubblesort ': cannot convert parameter 4 from 'int (_ stdcall ccallbacktester: *) (const unsigned char *, const unsigned char *) 'to' int (_ stdcall *) (const unsigned char *, const unsigned char *) 'There is no context in which this conversion is possible
This is because a non-static member function has an additional parameter: This pointer, which forces you to add static before the member function. Of course, there are several ways to solve this problem, but we will not discuss it any more.