Tips: 这篇文章比较长,像太阳能手电筒。总结结果在最后一段代码。想省时间的小友请下拉到最后copy!
众所周知,C/C++ 宏(macro) 不能递归,不能重载。
C99 标准的可变参数宏(__VA_ARGS__)无法用循环把它一个个参数分拆开来。
如果你要打印(或处理)一连串n个不同类型(type)的变量 。比如
void print(1, 2.0f, 'a', 3e-2, "UFO", 18u);
你可能会用
cout << 1 << 2.0f << 'a' << 3e-2 << "UFO" << 18u << endl;
但是你可能会觉得 << 很烦,而且只能用于输出。如果你要加个函数来处理每个参数,如typeid("UFO").name(),那输出一行就会非常长。
你可能会想到用 va_arg,很遗憾的告诉你,必须要求所有参数类型一样。否则,会对短字节类型做向上提升。
- #define printList(Func, type, cnt, ...) \
- { \
- va_list ls; \
- va_start(ls, cnt); \
- for (int i = 0; i < cnt; i++) { \
- cout << Func(va_arg(ls, type)) << " "; \
- } \
- cout << endl; \
- }
C++11标准引入了可变参数模板,你可以很方便的打印(或处理)一连串n个不同类型(type)的变量。
- template<class T>
- void printType(T &t) {cout << typeid(t).name() << endl;}
-
- template<class T, class... Args>
- void printType(T &t, Args&... rest) {
- cout << typeid(t).name() << ', ';
- printType(rest...);
- }
-
- // printType('a', 1, 2.0, "hello");
- // 输出:
- // char, int, double, const char*
在C++11标准前,如果你要打印一串n个不同类型(type)的变量,你可能需要重载n个不同的打印函数 void print(T a){}。T = T1, T2, ... Tn. 或者采用嵌套的宏一层一层叠加输出。
- #define print1(_1) cout << _1 << endl;
- #define print2(_1, _2) cout << _1 << ", "; print1(_2)
- #define print3(_1, ...) cout << _1 << ", "; print2(__VA_ARGS__)
- //...
- #define printN(_1, ...) cout << _1 << ", "; printN_1(__VA_ARGS__)
有N个变量,就有N个宏,如果N的数量很大,可能你要费好大劲来写这N个宏。
那么有没有办法利用 宏, 模板特化, 函数重载等手段,模拟构造一个类似C++11的可变参数打印宏呢?
直到我看到下面这篇文章,受到了启发。贴出来部分代码,
宏循环 - 标准替代GCC的##__ VA_ARGS__技巧?
- #define _SELECT(PREFIX, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- #define _print_1(fmt) printf(fmt "\n")
- #define _print_N(fmt, ...) printf(fmt "\n", ## __VA_ARGS__)
- #define println(...) _SELECT(_print, ## __VA_ARGS__, N, N, N, N, 1)(__VA_ARGS__)
-
- // 若要自定义处理宏。如自定义2个参数时的处理宏,
- // 则要添加_print_2()和修改 println 倒数第 1 个N为 2 ,参考my_println()。
- #define _print_2(fmt, ...) printf(fmt "\n", ## __VA_ARGS__)
- #define my_println(...) _SELECT(_print, ## __VA_ARGS__, N, N, N, 2, 1)(__VA_ARGS__)
-
- int main1(int argc, char *argv[]) {
- println("here is a log message");
- println("here is a log message with a param: %d", 42);
- my_println("here is a log message with a param: %d", 42);
- my_println("here is a log message with a param: %d %d %s %d", 42, 33, "abc", 13);
- return 0;
- }
-
- // _SELECT(PREFIX, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- // _SELECT(_print,'a', N, N, N, 2, 1 ) ('a')
这里_SELECT()宏就像一台秤上的尺子(Ruler),SUFFIX就像尺子上的游标。开始__VA_ARGS__为1个参数时,SUFFIX对应 到 1 的位置。只要加一个参数,SUFFIX就前移一个位置,对应到相应的数字上。借助这条尺子,可以根据不同参数个数,构造不同的打印宏 _print_1, _print_2, ...。没有构造,则默认调用_print_N()。
那么,我们是否可以利用这条尺子,来构造一套宏,组成不同参数个数的调用宏呢?当然可以。但我们不能像前面那样使用"层叠宏”那么stupid的方式。想像一下,能否使用幂增的方式,利用 m 个宏,来构造适配 2^m 个不同参数(个数可变)的调用方式呢?下面给出一个例子:
- #define _SELECT_10(PREFIX, _N, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
-
- template<typename T>
- void _output_0(T _1){
- cout << _1 << ", ";
- }
- void _output_0(){}
-
- #define _output_() cout << "None Args!" << endl;
- #define _output_N(...) cout << "Args' count overflow!\n";
-
- #define _output_1(_1) cout << _1;
- #define _output_2(_1, ...) _output_1(_1) cout << ", "; _output_0(__VA_ARGS__);
- #define _output_4(_1, _2, ...) _output_2(_1, _2) _output_2(__VA_ARGS__)
- #define _output_8(_1, _2, _3, _4, ...) _output_4(_1, _2, _3, _4) _output_4(__VA_ARGS__)
-
- #define output(...) _SELECT_10(_output, ##__VA_ARGS__, N, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1)(__VA_ARGS__)
- #define outputLn(...) output(__VA_ARGS__) cout << endl;
-
- // output(1) => SUFFIX=1 => _output_1()
- // output(1, 2) => SUFFIX=2 => _ouput_2(1, 2) => _output_1(1) cout<<","; _output0(2)
- // output(1, 2, 3) => SUFFIX=4 => _output_4(1, 2, 3) => _output_2(1, 2) _output_2(3)
- // => _output_2(1, 2) _output_1(1) cout < ","; _output_0();
- // => _output_1(1) cout < ","; _output_0(2); _output_1(1) cout < ","; _output_0();
从下面尺子和游标对齐可以看出,
- // 0 个参数时:
- _SELECT_10(PREFIX,
- _N, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- _SELECT_10(_output,
- N, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1)() => _output_
-
- // 1 个参数时:
- _SELECT_10(PREFIX,
- _N, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- _SELECT_10(_output,
- '1', N, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1 ) ('1') => _output_1('1')
-
- // 2 个参数时:
- _SELECT_10(PREFIX,
- _N, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- _SELECT_10(_output,
- '1','2', N, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1 ) ('1','2')
- => _output_2 ('1','2')
-
- // 3 个参数时:
- _SELECT_10(PREFIX,
- _N, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- _SELECT_10(_output,
- '1','2','3', N, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1 ) ('1','2','3')
- => _output_4 ('1','2','3')
-
当有1个参数时,SUFFIX对应 1,应用宏_output_1();
当有2个参数时,SUFFIX对应 2,应用宏_output_2();
当有3个参数时,SUFFIX对应 4,应用宏_output_4();
也就是说,当有 x个参数,2^(n-1) < x <= 2^ n,时,我们应用 _output_[2^n]() 这个宏,
并逐步一分为二,扩展开来,最终扩展到剩下一个参数时,则把这个参数打印出来。
这是我们设想一个的理想的状态,实际编程中编译器会提示错误。因为,比如5个参数时,
output('1', '2', '3', '4', '5')扩展为:
_output8('1', '2', '3', '4', '5') => _output4('1', '2', '3', '4') _output4('5')
这时编译器会提示_output4('5') 存在错误。只传入一个参数,不能匹配有两个固定参数的宏_output4(_1, _2, ...) 。也许你会说那就_output4(_1, _2, ...) 改成 _output4(_1, ...)。但这样一个就没法把4个参数时拆分成 '1','2' 和 '3','4'两拨了。就无法实现让子规模减半了。
聪明的你,也许已经想到了。那就把 _output4('5') 改写成 _output4('5', "") , 多了一个空串("")?没关系,反正最后输出空串(cout << "")也没改变什么。其实不然,你看_output2宏,后面会多输出一个 ',' 。也许你觉得行尾多输出一个','也没什么。其实不然,看有10个参数时的扩展
- #define _output_1 (_1, ...) cout << _1 << ", ";
- #define _output_2 (_1, ...) _output_1(_1) _output_1(__VA_ARGS__)
- #define _output_4 (_1, _2, ...) _output_2(_1, _2) _output_2(__VA_ARGS__)
- #define _output_8 (_1, _2, _3, _4, ...) \
- _output_4 (_1, _2, _3, _4) _output_4(__VA_ARGS__, "")
- #define _output_16(_1, _2, _3, _4, _5, _6, _7, _8, ...) \
- _output_8 (_1, _2, _3, _4, _5, _6, _7, _8) _output_8(__VA_ARGS__, "", "", "")
-
- _output('1', '2', ... ,'10')
- => _output_16('1', '2', ... ,'10')
- => _output_8('1', '2', ... ,'8') _output_8('9', '10', "", "", "")
- => _output_8('1', '2', ... ,'8') _output_4('9', '10', "", "") _output_4("", "")
- => _output_8('1', '2', ... ,'8') _output_2('9', '10') _output_2("","") _output_2("","")
- => _output_8('1', '2', ... ,'8') _output_2('9', '10') \
- _output_1(""), _output_1(""), _output_1(""), _output_1("")
这后面多了4个_output_1(""), 就会多输出4个 ","。这已经不是你能忍受的了。目前我没有较好的办法来解决这个问题。只能在最后_output_1("") 时, 判断 参数=="" 时,不输出 ","。
- #ifdef __GNUC__
- #define _output_1(_1, ...) if (string(typeid(_1).name()) != "A1_c") cout << ", " << _1;
-
- // typeid("").name()) = "A1_c" // 环境 gcc (GCC) 10.2.0
- #elif defined(__MSC_VER__)
- #define _output_1(_1, ...) if (!(string(typeid(_1).name()) == "const char*" && _1 == "")) cout << ", " << _1;
-
- // typeid("").name()) = "const char*" // 环境 VS2010
- #endif
基于上面的构造形式,在打印多个参数时,最后行尾总会留下一个","
那么,我们可以把参数数列拆分为如:
1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15
先打出 1: cout << 1;再打出其它:_output(_1) cout << " ," << _1
以下我贴出完整代码:
- // 可变参数宏打印 (Variadic Macros "__VA_ARGS__")
- // 虽然宏不可以递归和重载,但我发明了“倍增宏扩展”方式。
- #include
-
- #include
- #include
- using namespace std;
-
- // vs里,如果 ... 表示的内容为空,__VA_ARGS__ 也为空,但 __VA_ARGS__仍占一个参数位置
- // GCC里 上面的情况不会出现,能正常推导宏
- // output 最多可输入16+1=17个参数
- #define NONE_ARGS ", None Args!\n"
- #define ARGS_OVERFLOW ", Arg's count overflow!\n"
- #define _output_() cerr << NONE_ARGS; cout << NONE_ARGS;
- #define _output_N(...) cerr << ARGS_OVERFLOW; cout << ARGS_OVERFLOW;
- #define _RULER_16(PREFIX, _N, _16, _15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- #define _output_0()
- #define _output_1(_1, ...) if (typeid(_1) != typeid("")) cout << ", " << _1;
- #define _output_2(_1, ...) _output_1(_1) _output_1(__VA_ARGS__)
- #define _output_4(_1, _2, ...) _output_2(_1, _2) _output_2(__VA_ARGS__, "")
- #define _output_8(_1, _2, _3, _4, ...) _output_4(_1, _2, _3, _4) _output_4(__VA_ARGS__, "", "")
- #define _output_16(_1, _2, _3, _4, _5, _6, _7, _8, ...) _output_8(_1, _2, _3, _4, _5, _6, _7, _8) _output_8(__VA_ARGS__, "", "", "", "")
- #define output(_1, ...) cout << _1; _RULER_16 (_output, ## __VA_ARGS__, N, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1, 0)(__VA_ARGS__)
- #define outputLn(...) output(__VA_ARGS__) cout << endl;
-
- int main() {
- cout << sizeof("") << " " << typeid("").name() << " " << typeid(typeof "").name() << endl;
-
- outputLn(1);
-
- outputLn(1, 2.0f);
- outputLn(1, 2.0f, 'a');
- outputLn(1, 2.0f, 'a', 3e-2);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO");
-
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101);
-
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l, -3.1415926l);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l, -3.1415926l, 0x12345l);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l, -3.1415926l, 0x12345l, 0xafe90cful);
-
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull, 3.14e-1);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull, 3.14e-1, 9876543210llu);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull, 3.14e-1, 9876543210llu, '\0');
-
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, 0b110101, 9283832l, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull, 3.14e-1, 9876543210llu, '\0', 0.0);
- return 0;
- }
-
- /*
- 输出:
- 1, 2, a, 0.03, UFO, 18, 48, 241, 53, 9283832
- 1, 2, a, 0.03, UFO, 18, 48, 241, 53, 9283832, -3.14159
- 1, 2, a, 0.03, UFO, 18, 48, 241, 53, 9283832, -3.14159, 74565
- 1, 2, a, 0.03, UFO, 18, 48, 241, 53, 9283832, -3.14159, 74565, 184455375
- 1, 2, a, 0.03, UFO, 18, 48, 241, 53, 9283832, -3.14159, 74565, 184455375, 1234567890
- 1, 2, a, 0.03, UFO, 18, 48, 241, 53, 9283832, -3.14159, 74565, 184455375, 1234567890, 0.314
- 1, 2, a, 0.03, UFO, 18, 48, 241, 53, 9283832, -3.14159, 74565, 184455375, 1234567890, 0.314, 9876543210
- 1, 2, a, 0.03, UFO, 18, 48, 241, 53, 9283832, -3.14159, 74565, 184455375, 1234567890, 0.314, 9876543210,
- 1, Arg's count overflow!
- */
最后,总结一下复杂度,假设要打印 N = 2^n个不同类型的参数。
采用幂增宏则需要写O(logN)行宏,每行最长O(3N/2)个参数,3个宏名.
采用层叠宏则需要写O(N)行宏, 每个行最长O(4)个参数,2个宏名。
以宏名长度为20(#define _output_1024),参数长度7(, _1024)计算。
相比
- logN * (3*20 + 7 * 3N/2) V.S N * (2*20 + 7 * 4)
- 60logN + logN * 21N/2 V.S 68*N
- (120+21N) * logN V.S 136*N
- (120 + 21*2^n) * n V.S 136 * 2^n
- 120n + 21n * 2^n V.S 136 * 2^n
- f(n) = 120n + 21n * 2^n - 136 * 2^n
- 当f(n) > 0, 左边复杂度 > 右边复杂度;
- 当f(n) < 0, 左边复杂度 < 右边复杂度
当n >= 6时, "幂增宏"复杂度都永远大于"层叠宏"。但其实像
_RULER_16(PREFIX, _N, _16, _15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...)
这样的长串,身为程序员,我们可以先写个函数,打印出 “_16, _15, ... _0”,再复制粘贴上去就OK了,或者创建个宏来表示。记得以前看过类似的,boost库把这长串用verctor容器装载起来了,但目前要找到相应的代码有点困难,希望有人能给我留言,告诉我是boost的哪个文件。至此,“完美”~~解决“可变参数宏”打印方案。
上述代码中提到
// vs里,如果 ... 表示的内容为空,__VA_ARGS__ 也为空,但 __VA_ARGS__仍占一个参数位置
这难道就是数学上的“诡辩”: 空集非空!
在vs2010下测试,
outputLn(1) 会展开成 cout << 1; cout << ' ,' << ; //会报错
就是说即使没有第2个参数,__VA_ARGS__ 也为空,但 __VA_ARGS__仍占一个参数位置
SUFFIX被推到 SUFFIX=1 的位置。
解决方法是,在一开始就给加个末尾空串 "",让__VA_ARGS__ 不为空。
- #define _output_1(_1, ...) if (not_equal(_1, "")) cout << " ," << _1;
- //... ...
- #define outputAdd(...) _RULER_16 (_output, ## __VA_ARGS__, N, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1, 0)(__VA_ARGS__)
- #define output(...) outputAdd(__VA_ARGS__, "")
- #define outputLn(...) output(__VA_ARGS__) cout << endl;
第二个方法是按
1, 2, 3, 4, 5, 6, 7, 这样的方案打印参数序列,即末尾会多出一个","。主要代码如下:
- #define _output_1(_1, ...) if (not_equal(_1, "")) cout << _1 << ", ";
- //... ...
- #define output(...) _RULER_16 (_output, ## __VA_ARGS__, N, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1, 0)(__VA_ARGS__)
- #define outputLn(...) output(__VA_ARGS__) cout << endl;
第三个方法比较“银蛋”了。参考 expand_ 会把宏参数会先尽可能展开后再进行替换。
- #define expand_(...) __VA_ARGS__
- #define bench_pattern(...) "",##__VA_ARGS__, "" // 当参数为空,不是吃掉逗号,而是把逗号换成空格
- #define second__(_1, _2, ...) _2
- #define second_(...) expand_(second__(__VA_ARGS__))
- #define bench_first(...) second_(bench_pattern(__VA_ARGS__)) // 模式: 取第1个参数,若为空,返回 ""
- #define _1st bench_first
造一个串 "", ##__VA_ARGS__, "", 当参数为空,取_2得 "", 当参数不为空,则取_2,实则取参数列表里第一个参数。实测得_1st()="", _1st(1)=1,_1st(1,2)=1。
expand()顺带把用__VA_ARGS__传参数时不能分割出前n个参数的问题解决了。只要传递给下一个宏前,把想要扩展的__VA_ARGS__用expand()套住。可以在最外层套,比如expand(your_macro(__VA_ARGS__))。最后贴上在gcc 10.0,vs2010,vs2019下测试通过的代码。
- // output 最多可输入16+1=17个参数
- #if defined(_MSC_VER) && (_MSC_VER >= 1600) // >= vs2010, 静态断言需要C++11标准支持,但在vs2019下要求我开启 "/std:c++17"
- #define CAN_STATIC_ASSERT
- #elif defined(__GNUC__) && (GCC_VERSION >= 40300)// >= gcc 4.3.0
- #define CAN_STATIC_ASSERT
- #endif
-
- // #define _MSC_VER 1600
-
- #ifdef CAN_STATIC_ASSERT
- #define _output_() static_assert("None Args!" && 0);
- #define _output_N(...) static_assert("Arg's count overflow!" && 0);
- #else
- #define _output_() assert("None Args!" && 0);
- #define _output_N(...) assert("Arg's count overflow!" && 0);
- #endif
-
- // 这里利用的原理是:
- // (1) expand_ 会把宏参数会先尽可能展开后再进行替换。
- // (2) 遇到#或##时,其相连的宏参数不会展开,然而这不意味着这个宏参数本身不会展开,其他部分用到这个宏参数的地方还是会展开的。
- // (3) 对当前宏展开完成后不会重新扫描一遍当前字符串,但可以另起一个宏,嵌套当前宏,让另一个宏去做扫描
- // boost-preprocessor (github)
- // https://www.codenong.com/cs106877864/
- //
- #if defined(_MSC_VER) && (_MSC_VER < 1920) // < vs2019
- //
- #define expand_(...) __VA_ARGS__
- #define E_ expand_
- //
- #define bench_pattern(...) "", ## __VA_ARGS__, "" // 当参数为空,不是吃掉逗号,而是把逗号换成空格
- #define bench_pattern2(...) bench_pattern(__VA_ARGS__)// 再套一层,可去掉替换逗号后的空格
- #define second__(_1, _2, ...) _2
- #define second_(...) expand_(second__(__VA_ARGS__))
- #define bench_first(...) second_(bench_pattern(__VA_ARGS__)) // 模式: 取第1个参数,若为空,返回 ""
- #define _1st bench_first
- //
- #define rest__(_1, ...) __VA_ARGS__
- #define rest_(...) expand_(rest__(__VA_ARGS__))
- #define _rst rest_
- //
- #define _ruler_16(PREFIX, _N, _16, _15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- #define _output_0()
- #define _output_x(_1) if (typeid(_1) != typeid("")) cout << ", " << _1;
- #define _output_1(...) _output_x(_1st(__VA_ARGS__))
- #define _output_2(_1, ...) _output_1(_1) _output_1(__VA_ARGS__)
- #define _output_4(_1, _2, ...) _output_2(_1, _2) E_(_output_2(__VA_ARGS__, ""))
- #define _output_8(_1, _2, _3, _4, ...) _output_4(_1, _2, _3, _4) E_(_output_4(__VA_ARGS__, "", ""))
- #define _output_16(_1, _2, _3, _4, _5, _6, _7, _8, ...) _output_8(_1, _2, _3, _4, _5, _6, _7, _8) E_(_output_8(__VA_ARGS__, "", "", "", ""))
- // outputQ(), 空参数仍会占用1个参数位置,但 _1st()会把空参数转为 ""
- #define outputQ(...) E_(_ruler_16(_output, ## __VA_ARGS__, N, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1, 0)(__VA_ARGS__))
- #define output(...) cout << _1st(__VA_ARGS__); outputQ(_rst(__VA_ARGS__))
- #define outputLn(...) output(__VA_ARGS__) cout << endl
- //
- #else // _MSC_VER >= vs2019
- #define _ruler_16(PREFIX, _N, _16, _15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, SUFFIX, ...) PREFIX ## _ ## SUFFIX
- #define _output_0()
- #define _output_1(_1, ...) if (typeid(_1) != typeid("")) cout << ", " << _1;
- #define _output_2(_1, ...) _output_1(_1) _output_1(__VA_ARGS__, "")
- #define _output_4(_1, _2, ...) _output_2(_1, _2) _output_2(__VA_ARGS__, "")
- #define _output_8(_1, _2, _3, _4, ...) _output_4(_1, _2, _3, _4) _output_4(__VA_ARGS__, "", "")
- #define _output_16(_1, _2, _3, _4, _5, _6, _7, _8, ...) _output_8(_1, _2, _3, _4, _5, _6, _7, _8) _output_8(__VA_ARGS__, "", "", "", "")
- #define output(_1, ...) cout << _1; _ruler_16(_output, ## __VA_ARGS__, N, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 4, 4, 2, 1, 0)(__VA_ARGS__)
- #define outputLn(...) output(__VA_ARGS__) cout << endl
- #endif
-
-
- int main() {
-
- #if defined(_MSC_VER) && (_MSC_VER < 1920) // < vs2019
- #define _9876543210ULL 9876543210Ui64
- #else
- #define _9876543210ULL 9876543210llu
- #endif
-
- // outputLn();
- outputLn(1);
-
- outputLn(1, 2.0f);
- outputLn(1, 2.0f, 'a');
- outputLn(1, 2.0f, 'a', 3e-2);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO");
-
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear");
-
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll, -3.1415926l);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll, -3.1415926l, 0x12345l);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll, -3.1415926l, 0x12345l, 0xafe90cful);
-
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull, 314159E-5L);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull, 314159E-5L, _9876543210ULL);
- outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull, 314159E-5L, _9876543210ULL, '\0');
-
- // outputLn(1, 2.0f, 'a', 3e-2, "UFO", 18u, 060, 0xF1, "hello, " "d" "ear", 9283832ll, -3.1415926l, 0x12345l, 0xafe90cful, 1234567890ull, 314159E-5L, _9876543210ULL, '\0', 0.0);
-
- return 0;
- }
好,时间到,下课!同学们记得做课后作业 :假设让你来设计,按斐波那契数列设计一个倍增宏。你能否写出来?