C语言的宏

C编译器提供的相关参数

  • #
  • ##
  • FILELINE
  • …和VA_ARGS
  • ##VA_ARGS

“#”

把参数转换成字符串

1
2
3
4
5
#define STR(arg) #arg
NSLog(@"%s", STR(no qutoes);
#define OCSTR(arg) @#arg
NSLog(OCSTR(This is a string));

“##”

连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#define DEF_VAR(type, name) type type##_##name
DEF_VAR(int, a) = 5;
NSLog(@"%d", int_a);
```
## 一些有用的参数
```C
NSLog(@"%s\n%d\n%s\n%s\n%s\n%s",
__FILE__, //⽂文件全路路径
__LINE__, //行号
__DATE__, //预编译时的日期
__TIME__, //预编译时的时间
__TIMESTAMP__, //预编译⽇日期时间
__FUNCTION__); //当前函数
```
## 可变参数宏
### “__VA_ARGS__”
```C
#define MY_LOG(str, ...) NSLog(@"【"__FILE__":%d】"str, __LINE__, __VA_ARGS__)
MY_LOG("%d %d %d", 1, 2, 3);
"%d %d %d" > str
1, 2, 3 > __VA_ARGS__
"【/blalbal/:12】1 2 3"

“##VA_ARGS

如果只有一个参数呢

1
2
3
4
5
6
MY_LOG("only one parameters");
NSLog(@"【""/file/path/file.m"":%d】""only one parameters", 42, );
#define MY_LOG(str, ...) NSLog(@"【"__FILE__":%d】"str, __LINE__, ##__VA_ARGS__)
##__VA_ARGS__ 为空时会吞掉前面的引号

应用

KEY_PATH 提示

1
2
#define KEY_PATH(CLASS, PATH) \
(((void)(NO && ((void)[CLASS new].PATH, NO)), @# PATH))
1
2
3
4
5
6
7
8
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{
KEY_PATH(SomeClass, property1): @"property_1",
KEY_PATH(SomeClass, property2): @"property_2",
KEY_PATH(SomeClass, property3): @"property_3",
};
}
1
(((void)(NO && ((void)[SomeClass new].property1, NO)), @"property1")): @"property_1",

玩转可变参数

ARG_AT

1
2
3
4
5
6
7
8
#define ARG_AT(INDEX, ...) _ARG_AT##INDEX(__VA_ARGS__)
#define _ARG_AT0(_0, ...) _0
#define _ARG_AT1(_0, _1, ...) _1
#define _ARG_AT2(_0, _1, _2, ...) _2
#define _ARG_AT3(_0, _1, _2, _3, ...) _3
#define _ARG_AT4(_0, _1, _2, _3, _4, ...) _4
#define _ARG_AT5(_0, _1, _2, _3, _4, _5, ...) _5
1
2
3
4
5
int value = ARG_AT(3, 100, 200, 300, 400);
int value = _ARG_AT##3(100, 200, 300, 400);
int value = _ARG_AT3(100, 200, 300, 400);
// #define _ARG_AT3(_0, _1, _2, _3, ...) _3
int value = 400;

PS: 参数<=5

ARG_COUNT

1
2
#define ARG_COUNT(...) \
ARG_AT(5, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0)

DEC

1
#define DEC(N) \ ARG_AT(N, -1, 0, 1, 2, 3, 4)

vs

1
#define DEC(N) N - 1

为啥?
预编译后就能拿到结果

IS_EQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define IS_EQ(A, B) _CONCAT(_IS_EQ, A)(B)
#define _CONCAT(A, B) A ## B
#define _IS_EQ0(B) _CONCAT(_IS_EQ0_, B)
#define _IS_EQ0_0 1
#define _IS_EQ0_1 0
#define _IS_EQ0_2 0
#define _IS_EQ0_3 0
#define _IS_EQ0_4 0
#define _IS_EQ0_5 0
#define _IS_EQ1(B) _IS_EQ0(DEC(B))
#define _IS_EQ2(B) _IS_EQ1(DEC(B))
#define _IS_EQ3(B) _IS_EQ2(DEC(B))
#define _IS_EQ4(B) _IS_EQ3(DEC(B))
#define _IS_EQ5(B) _IS_EQ4(DEC(B))

一步一步展开

1
2
3
4
5
6
7
8
9
10
IS_EQ(2, 3);
_CONCAT(_IS_EQ, 2)(3);
_IS_EQ ## 2(3);
_IS_EQ2(3);
_IS_EQ1(DEC(3));
_IS_EQ1(2);
_IS_EQ0(DEC(2))
_IS_EQ0(1)
_CONCAT(_IS_EQ0_, 1)
_IS_EQ0_1 0

综合应用-metamacro_foreach

libextobjc库中的宏metamacro_foreach

metamacro_foreach(MACRO, SEP, …)
参数有三部分,MACRO, SEP 和需要处理的数据 VAL
实现的功能是,把每个VAL和对应的INDEX通过MACRO处理,得到的结果通过SEP连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define stringify(INDEX, VALUE) \
# INDEX metamacro_stringify(VALUE)
metamacro_foreach(stringify,"-", 0, 1)
metamacro_foreach_cxt(metamacro_foreach_iter, "-", stringify, 0, 1)
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(0, 1))(stringify, "-", CONTEXT, 0, 1)
metamacro_concat(metamacro_foreach_cxt, 2)(stringify, "-", CONTEXT, 0, 1)
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
SEP \
MACRO(1, CONTEXT, _1)
metamacro_foreach_cxt2(stringify, "-", CONTEXT, 0, 1)
metamacro_foreach_cxt1(stringify, "-", CONTEXT, 0)"-"stringify(1, CONTEXT, 1)
metamacro_foreach_cxt0(stringify, "-", CONTEXT, 0)stringify(1, CONTEXT, 1)"-"stringify(1, CONTEXT, 1)
stringify(0, CONTEXT, 0)"-"stringify(1, CONTEXT, 1)
"00""-""11"
"00-11"

根据需要处理数据的个数,生成 metamacro_foreach_cxtN,metamacro_foreach_cxtN 里面还定义了 metamacro_foreach_cxt(N-1), 并且展开宏 MACRO(INDEX(N-1), CONTEXT, (VALN-1)),并且会有一个SEP在前面,然后依次展开 metamacro_foreach_cxt(N-2),metamacro_foreach_cxt(N-3),最后得到一个整体的结果
00-11