6. C++ TMP

先讲讲 C++宏编程
#define ADD_COMMA(arg1, arg2) arg1, arg2
ADD_COMMA(ADD_COMMA(1, 2), ADD_COMMA(3, 4)) // -> 1, 2, 3, 4
宏参数展开:在对宏进行展开的时候,如果宏的参数也是可以展开的宏,会先把参数完全展开,再展开宏

#define CONCAT(arg0, arg1) arg0 ## arg1
#define STRINGIZE(arg0) # arg0
CONCAT(STRING, IZE(Hello)) // -> STRINGIZE(Hello) -> "Hello"
重复扫描预处理器执行完一次宏展开之后,会重新扫描得到的内容,继续展开,直到没有可以展开的内容为止
一次宏展开,可以理解为先把参数完全展开(除非遇到 # 和 ##),再根据宏的定义,把宏和完全展开后的参数按照定义进行替换,再处理定义中的所有 # 和 ## 操作符。

#define ITER(arg0, arg1) ITER(arg1, arg0)
ITER(1, 2) // -> ITER(2, 1)
递归重入(Reentrancy):该宏成功展开了一次,也只会展开一次,不会再递归重入。换言之,宏的展开过程中,是不可自身递归重入的,如果在展开的过程中发现相同的宏在之前的递归中已经展开过,则不再展开,这是宏展开的其中一条重要的规则。禁止递归重入的原因也很简单,就是为了避免无限递归。

#define STRINGIZE(arg0) # arg0
STRINGIZE(STRINGIZE(a)) // -> "STRINGIZE(a)"
字符串符:# 操作符后面跟的宏参数,不会进行展开,会直接字符串化

#define CONCAT(arg0, arg1) arg0 ## arg1
CONCAT(Hello, CONCAT(World, !)) // -> HelloCONCAT(World, !)
不展开符:## 操作符前后的宏参数,都不会进行展开,而是会直接跟前面的内容先拼接在一起

#define LOG(format, ...) printf("log: " format, __VA_ARGS__)
LOG("Hello %s\n", "World") // -> printf("log: " "Hello %s\n", "World");
变长参数:宏可以接受变长参数,格式是 ...
由于变长参数有可能为空,空的情况下会导致编译失败,因此 C++ 20 引入了 __VA_OPT__,如果变长参数是空,则返回空,否则返回原参数

宏展开过程
略,之后补实现过程

宏编程实现
用宏可以实现很多与编程语言相近的效果,如()、IDENTITY(arg)、BOOL、concat、IF、for_each、while、比较、算术运算、数据结构等……
暂时用不到,之后再补

宏编程的优点是自由、万能,缺点是不好调试,其实大部分宏编程也能用Template Metaprogramming实现














comments powered by Disqus