Callback and enumeration in big cases, and callback function enumeration in big cases
I. Cause
(1) Next I wrote a blog post about function pointers and pointer functions.
(2) pointer application is the essence of C language programming,The callback function is an advanced application of function pointers in C language.
(3) callback functions can separate code implementations from function descriptions,Code reusability, especially code scalability
(4)To implement a function that is irrelevant to the type, you can use the callback function to achieve this purpose.Of course, you can also implement the corresponding method through generics.
(5)Allows a function to be called as a parameter., Very flexible,It can replace complex swich-case statements, especially when combined with enum (enumeration ).
Ii. Details of callback Functions
(1) In short,A callback function is a function called by a function pointer. If you pass the function pointer (the function entry address) to another function, when this function pointer is used to call the function to which it points, we will say that this function is a callback function.
(2) Why should I use a callback function? Let's take a look at a small example:
Node * Search_List (Node * node, const int value) { while (node != NULL) { if (node -> value == value) { break; } node = node -> next; } return node; }
Explanation: This function is used to find a specified value in a one-way linked list and return the node that saves the value. Its parameter is the pointer to the first node of the linked list and the value to be searched. But consider one problem: it can only apply to a linked list with an integer value. If we look for a string linked list, we have to write another function. In fact, most of the Code is the same as the current function, the type and comparison method of the second parameter are different.
Solution:
Write a function (callback function) to compare two values of the same type (write a comparison function to Solve the Problem)Then, a pointer pointing to this function is passed as a parameter to the lookup function, and The lookup function calls this comparison function to execute the comparison. Using this method, any type of value can be compared.
However, you must also pass a pointer to the value to be compared to the value itself, that is, a void * type parameter, which is passed to the callback function for final comparison.. This modification allows us to pass pointers of any type to the lookup function to complete the comparison of any type. This is the benefit of the pointer,
We cannot pass strings, arrays, or struct as parameters to functions, but the pointers to them can.
(3) changed code
NODE * Search_List (NODE * node, int (* compare) (void const *, void const *), void const * desired_value); {while (node! = NULL) {if (compare (node-> value_address), desired_value) = 0) {break;} node = node-> next;} return node ;} // note that the linked list NODE is defined as follows: typedef struct list {void * value_address; struct list * next;} NODE;
This definition allows a pointer of the NODE * type to point to a linked list NODE that stores any type of data. Value_address is a pointer to specific data,We define it as void * to indicate a pointer pointing to an unknown type, so that the linked list can store any type of data.The first parameter passed to the Search_List function can be uniformly represented as: NODE *,Otherwise, you need to write the lookup functions separately to store linked lists of different data types.
Now, The lookup function is irrelevant to the type because it does not perform actual comparison. Therefore, we must compile a comparison function for different types (just compile different comparison functions ), this is easy to implement,Because the caller knows the type of values contained in the linked list, if you create several linked lists that contain different types of values,Writing a comparison function for each type allows a single search function to act on all types of linked lists.
(4) Compilation of comparison functions
Int int_compare (void const * a, void const * B) {if (* (int *) a = * (int *) B) {return 0 ;} else {return-1 ;}} can be used as follows: desired_node = Search_List (root, int_compare, & desired_int_value); if you want to search in a string linked list, the following code completes the task: (of course, you can also compile a str_copare function to replace string. in h, strcmp) desired_node = Search_List (root, strcmp, "abcdefg"); the result is that the comparison executed by the library function strcmp is the same as what we need, but gcc will issue a warning: because the strcmp parameter is declared as const char * instead of void cons T *. <Strong> <span style = "font-size: 14px;"> </span> </strong>
Iii. Callback Function instances
(1) For example, implement a pocket calculator. The other part of the program has already read two numbers (op1 and op2) and one operand (operator ).
(2) The following code is used to test the operators and decide which function to call:
switch( oper ){ case ADD: result = add( op1, op2); break; case SUB: result = sub( op1, op2); break; case MUL: result = mul( op1, op2); break; case DIV: result = div( op1, op2); break; ......
However, for a novel calculator with hundreds of operators, this switch statement will be very long. Why do I need to call a function to perform these operations? Separating specific operations from the code of selected operations is a good design method. More complex operations will certainly be implemented using independent functions, because they may be long in length.
(3) Current Method:
To use the switch statement, the code of the operator must be an integer. If they are continuous integers starting from scratch, we can use a conversion table to implement the same task.. A conversion table is an array of function pointers. Two steps are required to create a conversion table. First, declare and initialize a function pointer array. The only thing to note is that the prototype of these functions appears before the declaration of this array.
Double add (double, double); double sub (double, double); double mul (double, double); double div (double, double );...... double (* oper_func []) (double, double) = {add, sub, mul, div ,...}; the correct order of function names in the initialization list depends on the integer code used in the program to represent each operator. This example assumes that ADD is 0, SUB is 1, MUL is 2, and so on.
The first step is to replace the previous switch statement with the following statement!
Result = oper_func [condition] (op1, op2 );
The handler selects the correct function pointer from the array, and the function call operator executes this function.
(4) The specific code is as follows:
# Include <iostream> using namespace std; double add (double, double); double sub (double, double); double mul (double, double); double div (double, double); // or other pointer double (* oper_func []) (double, double) = {add, sub, mul, div}; double calculate (double, double, double (* func) (double, double); int main () {double op1, op2; cin> op1> op2; int index = 0; // index can be changed as needed // double result = oper_func [index] (op1, op2 ); // Double result = calculate (op1, op2, oper_func [index]); cout <"result =" <result <endl; return (0 );} double calculate (double op1, double op2, double (* func) (double, double) {return func (op1, op2);} double add (double op1, double op2) {return op1 + op2;} double sub (double op1, double op2) {return op1-op2;} double mul (double op1, double op2) {return op1 * op2 ;} double div (double op1, double op2) {If (op2! = 0) return op1/op2; return-1; // Failed} <strong> </strong>
Of course, you can also use enumeration to achieve more optimized methods.For more information, see blog.