0%

cpp foreach macro

showcase

  1. EqualToAnyOf
    1
    2
    3
    // 注意 递归单元
    int var = 3;
    EqualToAnyOf(var, 1, 2, 3); // 0 || (var == 1) || (var == 2) || (var == 3)
  2. MAKE_ENUM
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    MAKE_ENUM(EE,"name_prefix",E1,E2,E3);
    // 展开到
    enum class EE {E1, E2,E3 };
    const char *GetStr(EE e)
    {g
    switch (e)
    {
    case EE::E1:
    return "name_prefix" "E1";
    case EE::E2:
    return "name_prefix" "E2";
    case EE::E3:
    return "name_prefix" "E3";
    default:
    return "name_prefix" "";
    }
    };

介绍

1
2
3
4
// prototype
#define FOREACH(pps,...) ...
// acutually params
#define FOREACH(pps,it_operator,...)

pps: 打包参数,形如 (arg1), (arg1,arg2)
it_operator: 迭代宏函数,如此使用:ite_operator(pps,it)
...: 迭代参数集合,展开后作为 ite_oprator的最后一个参数

实现

辅助宏函数

  1. __Choose30th

    1
    2
    3
    4
    #define __Choose30th(_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,
    ...) _30
  2. ID
    msvc默认的宏展开有一点区别于gcc like编译器,所以老是需要用ID去做展开, 当然msvc也支持相关选项切换到gcc like的宏展开行为

    1
    2
    #define ID(_1, ...) _1
    #define IDx(...) __VA_ARGS__
  3. Callx + CallxPPS

    1
    2
    #define Callx(f, ...) IDx(f(__VA_ARGS__))
    #define CallxPPS(f, _1) IDx(f _1)

    两种形式的宏函数代理调用
    因为当我遇到 MacroFunc(IDx pps, otherArgs)时,外部套一层 IDx也无法使展开的pps作为分离参数传入MacroFunc

    感觉 ID只能辅助展开__VA_ARGS__,所以这里 Callx(MacroFunc, IDx pps, otherArgs) 将所有参数压缩成变长参数之后再由 Callx重新展开

过程

OK, finally.

Show me the code

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// clang-format off
#define __Choose30th(_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,\
...) _30
#define ID(_1, ...) _1
#define IDx(...) __VA_ARGS__
#define Callx(f, ...) ID(f(__VA_ARGS__))
#define CallxPPS(f, _1) ID(f _1)
#define FOREACH0(it_operator, pps, ...)
#define FOREACH1(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH0 (it_operator, pps, __VA_ARGS__))
#define FOREACH2(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH1 (it_operator, pps, __VA_ARGS__))
#define FOREACH3(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH2 (it_operator, pps, __VA_ARGS__))
#define FOREACH4(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH3 (it_operator, pps, __VA_ARGS__))
#define FOREACH5(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH4 (it_operator, pps, __VA_ARGS__))
#define FOREACH6(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH5 (it_operator, pps, __VA_ARGS__))
#define FOREACH7(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH6 (it_operator, pps, __VA_ARGS__))
#define FOREACH8(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH7 (it_operator, pps, __VA_ARGS__))
#define FOREACH9(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH8 (it_operator, pps, __VA_ARGS__))
#define FOREACH10(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH9 (it_operator, pps, __VA_ARGS__))
#define FOREACH11(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH10(it_operator, pps, __VA_ARGS__))
#define FOREACH12(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH11(it_operator, pps, __VA_ARGS__))
#define FOREACH13(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH12(it_operator, pps, __VA_ARGS__))
#define FOREACH14(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH13(it_operator, pps, __VA_ARGS__))
#define FOREACH15(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH14(it_operator, pps, __VA_ARGS__))
#define FOREACH16(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH15(it_operator, pps, __VA_ARGS__))
#define FOREACH17(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH16(it_operator, pps, __VA_ARGS__))
#define FOREACH18(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH17(it_operator, pps, __VA_ARGS__))
#define FOREACH19(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH18(it_operator, pps, __VA_ARGS__))
#define FOREACH20(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH19(it_operator, pps, __VA_ARGS__))
#define FOREACH21(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH20(it_operator, pps, __VA_ARGS__))
#define FOREACH22(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH21(it_operator, pps, __VA_ARGS__))
#define FOREACH23(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH22(it_operator, pps, __VA_ARGS__))
#define FOREACH24(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH23(it_operator, pps, __VA_ARGS__))
#define FOREACH25(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH24(it_operator, pps, __VA_ARGS__))
#define FOREACH26(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH25(it_operator, pps, __VA_ARGS__))
#define FOREACH27(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH26(it_operator, pps, __VA_ARGS__))
#define FOREACH28(it_operator, pps, _1, ...) IDx(it_operator(pps, _1) FOREACH27(it_operator, pps, __VA_ARGS__))
// clang-format on
#define FOREACH(pps, ...) \
ID(__Choose30th(__VA_ARGS__, \
FOREACH28, \
FOREACH27, \
FOREACH26, \
FOREACH25, \
FOREACH24, \
FOREACH23, \
FOREACH22, \
FOREACH21, \
FOREACH20, \
FOREACH19, \
FOREACH18, \
FOREACH17, \
FOREACH16, \
FOREACH15, \
FOREACH14, \
FOREACH13, \
FOREACH12, \
FOREACH11, \
FOREACH10, \
FOREACH9, \
FOREACH8, \
FOREACH7, \
FOREACH6, \
FOREACH5, \
FOREACH4, \
FOREACH3, \
FOREACH2, \
FOREACH1, \
FOREACH0)) \
IDx((pps, __VA_ARGS__))

Use Case

  1. EqualToAnyOf
1
2
3
#define __EqualToPlain(x, y) || (x == y)
#define __EqualToPacket(px, y) Callx(__EqualToPlain, IDx px, y)
#define EqualToAnyOf(val, ...) 0 FOREACH(__EqualToPacket, (val), __VA_ARGS__)
  1. MAKE_ENUM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define MAKE_ENUM_Declare(ClsName,...) enum class ClsName{__VA_ARGS__};
#define MAKE_ENUM_ITEM_Begin(ClsName) \
const char *GetStr(ClsName e) \
{ \
switch (e) \
{
#define MAKE_ENUM_ITEM(ClsName, Prefix, name) \
case ClsName::name: \
return Prefix #name;
// #define MAKE_ENUM_ITEM_PPS(pps, item) CallxPPS( Callx,(MAKE_ENUM_ITEM, IDx pps, item))
#define MAKE_ENUM_ITEM_PPS(pps, item) Callx(MAKE_ENUM_ITEM, IDx pps, item)
#define MAKE_ENUM_ITEM_End(ClsName, Prefix) \
default: \
return Prefix ""; \
} \
}
#define MAKE_ENUM(ClsName, Prefix, ...) \
MAKE_ENUM_Declare(ClsName, __VA_ARGS__) \
MAKE_ENUM_ITEM_Begin(ClsName) \
IDx(FOREACH(MAKE_ENUM_ITEM_PPS, (ClsName, Prefix), __VA_ARGS__)) \
MAKE_ENUM_ITEM_End(ClsName, Prefix);

附录

  1. 检查 预处理器输出

​ 即使是vs2022默认的智能感知引擎也可能无法在hover tip上正确显示预处理的结果,所以需要手动用编译器展开观察中间结果

1
2
3
4
5
6
7
8
9
10
11
12
13
# MSVC	>> foreach_macro.i
while($true)
{
cl /P foreach_macro.cpp
sleep 1
}

# CLANG >> stdout (or append "-o foreach_macro.i")
while($true)
{
clang -E foreach_macro.cpp
sleep 1
}