Many C + + books have advised us that C-language macros are the root of all evils, but things are not as bad as we think, like Goto. Macros have a big role in automatically generating code for us. If the template can produce various types of code for us (type substitution), then the macro can actually generate new code for us on the symbol (that is, symbol substitution, increment).
Some of the syntax questions about macros can be found on Google. Believe me, your knowledge of macro is not as much as you think. If you do not know the # and # #, also do not know Prescan, then you certainly do not understand the macro.
I explain a little bit about the grammar of the macro (saying that the grammar problem seems wrong, macro only with preprocessor, not with semantic analysis):
1. Macros can be defined like functions, such as:
#define MIN (x,y) (x but in actual use, only when you write min (), you must put parentheses, Min will be as a macro expansion, otherwise do not do any processing.
2. If the macro requires parameters, you can not pass, the compiler will give you a warning (macro parameters are not enough), but this will cause errors. as described in C + + books, the compiler (preprocessor) does not have enough grammar checks on macros, so you can do more of the checking work yourself.
3. Many programmers do not know # and # #
#符号把一个符号直接转换为字符串, for example:
#define STRING (x) #x
const char *STR = STRING (test_string); The content of STR is "test_string", which means that the # will add the following symbol directly to the double quotes.
# #符号会连接两个符号, resulting in a new symbol (lexical hierarchy), for example:
#define SIGN (x) int_# #x
int SIGN (1); Macros are expanded to become: int int_1;
4. Variable parameter macro, this is cool, it allows you to define a similar macro:
#define LOG (format, ...) printf (format, __va_args__)
LOG ("%s%d", str, count);
__VA_ARGS__ is a system-predefined macro that is automatically replaced with a parameter list.
5. What happens when a macro calls itself itself? For example:
#define TEST (x) (x + Test (x))
TEST (1); What's going to happen? To prevent unrestricted recursion, the syntax stipulates that when a macro encounters itself, it stops expanding, that is, when test (1) is expanded and a test is found during the expansion, this test is treated as a generic symbol. TEST (1)
The final expansion is: 1 + TEST (1).
6. Macro parameter Prescan, when a macro parameter is put into the macro body, this macro parameter will be all expanded first (with exception, see below). When the expanded macro parameter is put into the macro body, the preprocessor scans the newly expanded macro body for a second time and continues to expand. For example:
#define PARAM (x) x
#define ADDPARAM (x) int_# #x
PARAM (AddParam (1));
Because AddParam (1) is the macro argument for Param, the AddParam (1) is expanded to int_1 before the int_1 is placed in Param.
The exception is if you use # or # # for macro parameters in the Param macro, the macro parameters are not expanded:
#define PARAM (x) #x
#define ADDPARAM (x) int_# #x
PARAM (AddParam (1)); will be expanded to "AddParam (1)".
With such a rule, you can create an interesting technique: print out the way a macro is expanded so that you can analyze the code:
#define TO_STRING (x) to_string1 (x)
#define TO_STRING1 (x) #x
To_string First expands x all (if x is also a macro) and then passes it to to_string1 to string, and now you can:
const char *STR = to_string (PARAM (AddParam (1)); Go to the look of the PARAM unfold.
7. A very important addition: as I said in the 1th, if a macro like a function does not appear with parentheses in use, the preprocessor will simply treat the macro as a generic notation (that is, no processing).
Let's see how macros Help us generate code automatically. As I said, macros generate code at the symbol level. I'm parsing the Boost.function module because it uses a lot of macros (macro nesting, and then nesting), causing me to not read the code at all. Later, a small template library ttl was discovered, saying that it was a good reason to develop some small components to replace the partial boost (because boost is really too big). Similarly, this library also contains a function library.
The function here is the functor I mentioned earlier. The Ttl.function library uses a macro to automatically generate a lot of similar code:
#define TTL_FUNC_BUILD_FUNCTOR_CALLER (N)/
template< TypeName R, Ttl_tparams (n) >/
struct functor_caller_base# #n/
///...
The ultimate goal of the macro is to automatically generate many functor_caller_base templates by means of a call similar to Ttl_func_build_functor_caller (1):
Template struct FUNCTOR_CALLER_BASE1
Template struct FUNCTOR_CALLER_BASE2
Template struct FUNCTOR_CALLER_BASE3
///...
The core, then, is the Ttl_tparams (n) macro, which shows that the macro ultimately produces:
TypeName T1
TypeName T1, TypeName T2
TypeName T1, TypeName T2, TypeName T3
///...
We may wish to analyze the whole process of Ttl_tparams (n). Analysis macros mainly grasp some of the points I mentioned above can be. The following procedure I suggest you turn on the TTL code,
Related code files: function.hpp, macro_params.hpp, MACRO_REPEAT.HPP, MACRO_MISC.HPP, macro_counter.hpp.
So, here we go
Analytical process, layered analysis, layered, such as ttl_tparams (1):
#define TTL_TPARAMS (N) ttl_tparamsx (n,t)
=> TTL_TPARAMSX (1, T)
#define TTL_TPARAMSX (n,t) ttl_repeat (n, Ttl_tparam, Ttl_tparam_end, T)
=> ttl_repeat (1, Ttl_tparam, Ttl_tparam_end, T)
#define Ttl_tparam (n,t) typename t# #n,
#define Ttl_tparam_end (n,t) TypeName t# #n
#define TTL_REPEAT (N, M, L, p) ttl_append (Ttl_repeat_, Ttl_dec (n)) (m,l,p) Ttl_append (ttl_last_repeat_,n) (l,p)
Note that, Ttl_tparam, Ttl_tparam_end is also two macros, they are used as parameters for Ttl_repeat macros, and according to the Prescan rule, it seems that the two macros should be expanded and passed to Ttl_repeat. But, as I mentioned earlier, these two macros are Function-like macro, and are used with parentheses, not as macros if they are not bracketed. Therefore, when you expand Ttl_repeat, you should:
=> ttl_append (Ttl_repeat_, Ttl_dec (1)) (ttl_tparam,ttl_tparam_end,t) Ttl_append (ttl_last_repeat_,1) (
TTL_TPARAM_END,T)
The macro body looks very complicated, and it can be divided into two parts by careful analysis:
Ttl_append (Ttl_repeat_, Ttl_dec (1)) (ttl_tparam,ttl_tparam_end,t) and
Ttl_append (ttl_last_repeat_,1) (ttl_tparam_end,t)
First part of the analysis:
#define Ttl_append (x, y) ttl_append1 (x,y)//Start x,y and then connect x,y
#define TTL_APPEND1 (x, y) x # # Y
#define TTL_DEC (N) ttl_append (TTL_CNTDEC_, N)
Based on the principle of first expanding the parameters, the Ttl_dec (1) will be expanded first.
=> ttl_append (ttl_cntdec_,1) => ttl_cntdec_1
#define TTL_CNTDEC_1 0 Note that TTL_CNTDEC_ is not a macro, ttl_cntdec_1 is a macro.
=> 0, that is to say, Ttl_dec (1) is eventually expanded to 0. Back to the Ttl_append section:
=> Ttl_repeat_0 (ttl_tparam,ttl_tparam_end,t)
#define TTL_REPEAT_0 (M,L,P)
TTL_REPEAT_0 This macro is empty, then the first part of the above is ignored, and now there is only the second part:
Ttl_append (ttl_last_repeat_,1) (ttl_tparam_end,t)
=> ttl_last_repeat_1 (ttl_tparam_end,t)//Ttl_append combines ttl_last_repeat_ with 1
#define TTL_LAST_REPEAT_1 (M,p) m (1,p)
=> ttl_tparam_end (1, T)
#define Ttl_tparam_end (n,t) TypeName t# #n
=> TypeName T1 expanded.
Although we have analyzed it, but this is actually not what we want. We should get the author macros's programming ideas from those macros. A good use of macros may seem like some Chine, but he does make our code more automated.