The main function of macros is to simplify code writing and to simplify some areas where repeated code is needed to get code that looks more elegant. However, it is not easy to use macros well. Poor macros can easily cause disastrous consequences. This article will introduce some practical techniques for comparing macros.
First is the most commonly used techniques (http://blog.misakamm.org/p/209 ):
# Define MACROCAT (x, y) MACROCAT1 (x, y)
# Define MACROCAT1 (x, y) x # y
# Define TOSTRING (s) # s
MACROCAT concatenates x and y, and TOSTRING converts s to a string, for example, printf (TOSTRING (% s), TOSTRING (abcdefg ));
Then, we can do this because macros cannot be recursive, but we can do recursive simulation. For example, to generate a string consisting of n-bit binary numbers from small to large (using the previous macro ):
# Define BIN_0 (arg) TOSTRING (arg)
# Define BIN_1 (arg) BIN_0 (MACROCAT (arg, 0) "," BIN_0 (MACROCAT (arg, 1 ))
# Define BIN_2 (arg) BIN_1 (MACROCAT (arg, 0) "," BIN_1 (MACROCAT (arg, 1 ))
# Define BIN_3 (arg) BIN_2 (MACROCAT (arg, 0) "," BIN_2 (MACROCAT (arg, 1 ))
# Define BIN_4 (arg) BIN_3 (MACROCAT (arg, 0) "," BIN_3 (MACROCAT (arg, 1 ))
Int main ()
{
Puts (BIN_4 ());
Return 0;
}
Note that, for example, BIN_2 (), the actually expanded result is
"0" "0" "," 0 "" 1 "," 1 "" 0 "," 1 "1"
However, c/c ++ requires that strings written in this way will be merged into one during compilation, so puts can directly output the complete results.
If you want more bits, it's easy. If you don't mind, just copy the macro above and change the number.
However, it is troublesome to change several numbers in this case. Can you make it work better? For example, you only need to change the macro name?
At this time, we need to use a more skillful TRICK: Let each macro have one more parameter n, and then the preceding BIN_x uses MACROCAT to link it with numbers. Isn't that all right?
The idea is good, but the problem is that the macro does not have the ability to perform subtraction, and all you can do is replace it. How can I reduce the value by 1?
It is not difficult. See the following definition:
# Define DECVAL_1 0
# Define DECVAL_2 1
# Define DECVAL_3 2
# Define DECVAL_4 3
# Define DECVAL_5 4
# Define DECVAL_6 5
# Define DECVAL_7 6
# Define DECVAL_8 7
# Define DECVAL_9 8
# Define DECVAL (n) DECVAL _ # n
Well, with this powerful weapon, we can transform the original macro. We should first take the No. 0 and No. 1 macros for a knife:
# Define BIN_0 (n, arg) TOSTRING (arg)
# Define BIN_1 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BIN _, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
Do you understand what to replace? In this way, for the 2, 3, 4, and 5 numbers that follow, just copy the definition of 1 and change the macro name to solve the problem.
Questions:
The binary results generated here contain leading 0. How can we rewrite the results without leading 0?
This method can be used to generate a lot of similar code recursively. This technique is also very practical, but recursive construction is not easy. You need to think about it carefully. Otherwise, it is easy to make mistakes, pay special attention to the timing of macro development. Generally, MACROCAT1 macro is not used directly, because it is probably not the result you want.
Then, after the C99 standard (that is to say, the following content is incompatible with bc3/tc/vc6), the macro has a role: Variable Parameter number macro
For example, you can # define PRINTF (...) fprintf (stdout, _ VA_ARGS __)
_ VA_ARGS _ represents '... 'All parameters, so that you can easily redefine the output behavior of the function with variable parameters in the library function, such as redirecting printf to a file (although it can also be implemented using freopen, but I just want to say that minghong can do the same)
Well, the compiler will be introduced in the following sections, which are divided into two schools: vc School and gcc School (including clang/objc), because the two do not process the following code, it needs to be implemented using a slightly different macro. Currently, I only encounter these two schools.
The current purpose is this. Because _ VA_ARGS _ contains several parameters, how many parameters can I know?
For example, if you write a macro NUM_PARAMS () and write NUM_PARAMS (abc, a, d, e) in it, the result after replacement is 4. Can you do this?
Ad Time:
Http://blog.misakamm.org/p/209
After the advertisement, come back to the wonderful program
First, we will introduce the gcc solution:
# Define PP_NARG (...) PP_NARG _ (_ VA_ARGS __, PP_RSEQ_N ())
# Define PP_NARG _ (...) PP_ARG_N (_ VA_ARGS __)
# Define PP_ARG_N (\
_ 1, _ 2, _ 3, _ 4, _ 5, _ 6, _ 7, _ 8, _ 9, _ 10 ,\
_ 11, _ 12, _ 13, _ 14, _ 15, _ 16, N,...) N
# Define PP_RSEQ_N ()\
16,15, 14,13, 12,11, 10 ,\
9, 8, 7, 6, 5, 4, 3, 2, 1, 0
The solution is very beautiful, clever, and concise. I don't want to explain it more?
However, please note that this is the gcc solution. The above code in vc8, vc9, and vc2010 won't get the correct result. This is related to the macro Processing Method of vc.
Next we will provide the vc solution (the following are based on vc2008 and vc2010)
# Define BRACKET_L ()(
# Define BRACKET_R ())
# Define PP_NARG (...)\
PP_NARG _ (_ VA_ARGS __, PP_RSEQ_N ())
# Define PP_NARG _(...)\
PP_ARG_N BRACKET_L () _ VA_ARGS _ BRACKET_R ()
# Define PP_ARG_N (\
_ 1, _ 2, _ 3, _ 4, _ 5, _ 6, _ 7, _ 8, _ 9, _ 10 ,\
_ 11, _ 12, _ 13, _ 14, _ 15, _ 16, N,...) N
# Define PP_RSEQ_N ()\
16,15, 14,13, 12,11, 10 ,\
9, 8, 7, 6, 5, 4, 3, 2, 1, 0
Some parentheses are replaced.
The problem is that when the PP_NARG _ to PP_ARG_N parameters are passed, if explicit parentheses exist, the number of parameters is not calculated for the macro, only the number of parameters is determined by the number of explicit commas. As a result, _ VA_ARGS _ is passed as a parameter. After we replace the parentheses with macros, we can expand the macros without directly appearing. Then, we can expand the newly constructed macros, in this way, the parameter can be matched.
However, this cannot be done in gcc. After gcc expands the macro name, if it finds that the subsequent symbols are not explicit parentheses, the previous macro will not be expanded. These two different features make me do not know how to write macros to make the two schools compatible, correctly show what I want.
After explaining the differences between the two compilers, the same issue will not be explained later, but two pieces of code will be provided at the same time.
Another similar problem is that there are a number of parameters. If I want to process each parameter, what should I do?
For example, to implement a macro # define SPREAD (...), you need to link the items in the parameter into a string.
In the previous example, we have implemented the method of undefined parameter expansion. Now let's try recursive descent Type Expansion (gcc version ):
# Define SPREAD0 (arg) # arg
# Define SPREAD1 (arg,...) SPREAD0 (arg)
# Define SPREAD2 (arg,...) SPREAD0 (arg) SPREAD1 (_ VA_ARGS __,)
# Define SPREAD3 (arg,...) SPREAD0 (arg) SPREAD2 (_ VA_ARGS __,)
# Define SPREAD4 (arg,...) SPREAD0 (arg) SPREAD3 (_ VA_ARGS __,)
# Define SPREAD5 (arg,...) SPREAD0 (arg) SPREAD4 (_ VA_ARGS __,)
# Define SPREAD6 (arg,...) SPREAD0 (arg) SPREAD5 (_ VA_ARGS __,)
# Define SPREAD7 (arg,...) SPREAD0 (arg) SPREAD6 (_ VA_ARGS __,)
# Define SPREAD8 (arg,...) SPREAD0 (arg) SPREAD7 (_ VA_ARGS __,)
# Define SPREAD9 (arg,...) SPREAD0 (arg) SPREAD8 (_ VA_ARGS __,)
# Define SPREAD (...) SPREAD9 (_ VA_ARGS __)
Here, every time a layer is entered, a previous parameter is disassembled from _ VA_ARGS _ and the remaining parameter is given to the next layer.
Here, the details are _ VA_ARGS _ followed by a comma, which means to fill in an empty parameter to avoid the following parameter insufficiency.
Then we can use puts (SPREAD (1, 2, 3, 4); to test it.
Of course, you need to use the preceding method (gcc ):
# Define SPREAD0 (arg) # arg
# Define SPREAD1 (n, arg,...) SPREAD0 (arg)
# Define SPREAD2 (n, arg,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) (DECVAL (n), _ VA_ARGS __,)
# Define SPREAD3 (n, arg,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) (DECVAL (n), _ VA_ARGS __,)
# Define SPREAD4 (n, arg,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) (DECVAL (n), _ VA_ARGS __,)
# Define SPREAD5 (n, arg,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) (DECVAL (n), _ VA_ARGS __,)
# Define SPREAD6 (n, arg,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) (DECVAL (n), _ VA_ARGS __,)
# Define SPREAD7 (n, arg,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) (DECVAL (n), _ VA_ARGS __,)
# Define SPREAD8 (n, arg,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) (DECVAL (n), _ VA_ARGS __,)
# Define SPREAD9 (n, arg,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) (DECVAL (n), _ VA_ARGS __,)
# Define SPREAD (...) SPREAD9 (9, _ VA_ARGS __)
Vc version:
# Pragma warning (disable: 4003) // remove warning
# Define SPREAD0 (arg) # arg
# Define SPREAD1 (n, arg,...) SPREAD0 (arg)
# Define SPREAD2 (n, arg ,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) BRACKET_L () DECVAL (n), _ VA_ARGS __, BRACKET_R ()
# Define SPREAD3 (n, arg ,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) BRACKET_L () DECVAL (n), _ VA_ARGS __, BRACKET_R ()
# Define SPREAD4 (n, arg ,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) BRACKET_L () DECVAL (n), _ VA_ARGS __, BRACKET_R ()
# Define SPREAD5 (n, arg ,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) BRACKET_L () DECVAL (n), _ VA_ARGS __, BRACKET_R ()
# Define SPREAD6 (n, arg ,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) BRACKET_L () DECVAL (n), _ VA_ARGS __, BRACKET_R ()
# Define SPREAD7 (n, arg ,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) BRACKET_L () DECVAL (n), _ VA_ARGS __, BRACKET_R ()
# Define SPREAD8 (n, arg ,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) BRACKET_L () DECVAL (n), _ VA_ARGS __, BRACKET_R ()
# Define SPREAD9 (n, arg ,...) SPREAD0 (arg) MACROCAT (SPREAD, DECVAL (n) BRACKET_L () DECVAL (n), _ VA_ARGS __, BRACKET_R ()
# Define SPREAD (...) SPREAD9 BRACKET_L () 9, _ VA_ARGS __, BRACKET_R ()
The above is just a fuzzy expansion. because the number of parameters is unknown, macro parameters will be left blank, so the vc compiler gives a warning.
If we use the previous technique to analyze the macro with an indefinite number of parameters, the combination of the macro and the macro will generate greater power, and we can achieve precise expansion, in the definition of the SPREAD macro, replace the macro PP_NARG (_ VA_ARGS _) with 9, so the comma after _ VA_ARGS _ can be removed, you can also simplify the code and avoid extra characters that you don't want after expansion.
Test Question 1:
Define a macro # define printf so that it can replace printf (str, a, B, c) with std: cout <a <B <c <std :: endl;
The number of parameters is uncertain. You do not need to consider the str content, but suppose there are no more than 10 parameters.
Http://blog.misakamm.org/p/209
The power of macros is even more powerful. When macros are combined with C ++ template programming, the real fear is coming...
Test Question 2:
Before C ++ 0x, the template has no variable parameters, so when multiple parameters are required, you have to solve them manually, or smart people can use the template to generate multi-parameter template code. Let's try this and see where the difficulty of the problem is increased. For example, if you generate a template function named sum, you can accept 1-10 parameters and return the result of adding these parameters.
Attached:
Answers to the first question:
# Define BINARY_E0 (n, arg) TOSTRING (arg)
# Define BINARY_E1 (n, arg) MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BINARY_E2 (n, arg) MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BINARY_E3 (n, arg) MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BINARY_E4 (n, arg) MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BINARY_E5 (n, arg) MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BINARY_E6 (n, arg) MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BINARY_E7 (n, arg) MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BINARY_E8 (n, arg) MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 0 ))\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BINARY_ENUM (n) MACROCAT (BINARY_E, n) (n ,)
# Define BIN_0 (n, arg) TOSTRING (arg)
# Define BIN_1 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), arg )\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BIN_2 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), arg )\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BIN_3 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), arg )\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BIN_4 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), arg )\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BIN_5 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), arg )\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BIN_6 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), arg )\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BIN_7 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), arg )\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BIN_8 (n, arg) MACROCAT (BIN _, DECVAL (n) (DECVAL (n), arg )\
"," MACROCAT (BINARY_E, DECVAL (n) (DECVAL (n), MACROCAT (arg, 1 ))
# Define BIN_ENUM (n) "0" MACROCAT (BIN _, n) (n ,)
Test code: puts (BIN_ENUM (8 ));
Answers are not provided for test questions.
Source: http://blog.misakamm.org/p/209