代码之家  ›  专栏  ›  技术社区  ›  Tarski

如何使用C预处理器编写while循环?

  •  63
  • Tarski  · 技术社区  · 17 年前

    我是从教育/黑客的角度问这个问题的,(我真的不想这样编码)。

    是否可以只使用 C

    8 回复  |  直到 8 年前
        1
  •  128
  •   Paul Fultz II    8 年前

    #define EMPTY()
    #define DEFER(id) id EMPTY()
    #define OBSTRUCT(id) id DEFER(EMPTY)()
    #define EXPAND(...) __VA_ARGS__
    
    #define A() 123
    A() // Expands to 123
    DEFER(A)() // Expands to A () because it requires one more scan to fully expand
    EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan
    

    为什么这很重要?当宏被扫描并展开时,它会创建一个禁用上下文。此禁用上下文将导致引用当前正在扩展的宏的标记被涂成蓝色。因此,一旦将其绘制为蓝色,宏将不再展开。这就是宏不递归展开的原因。但是,禁用上下文仅在一次扫描期间存在,因此通过延迟扩展,我们可以防止宏被涂成蓝色。我们只需要对表达式应用更多扫描。我们可以用这个 EVAL

    #define EVAL(...)  EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
    #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
    #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
    #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
    #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
    #define EVAL5(...) __VA_ARGS__
    

    接下来,我们定义一些用于执行某些逻辑(如if等)的运算符:

    #define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
    #define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
    
    #define CHECK_N(x, n, ...) n
    #define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
    
    #define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
    #define NOT_0 ~, 1,
    
    #define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
    #define COMPL_0 1
    #define COMPL_1 0
    
    #define BOOL(x) COMPL(NOT(x))
    
    #define IIF(c) PRIMITIVE_CAT(IIF_, c)
    #define IIF_0(t, ...) __VA_ARGS__
    #define IIF_1(t, ...) t
    
    #define IF(c) IIF(BOOL(c))
    

    现在有了这些宏,我们可以编写递归 WHILE 宏。我们使用 WHILE_INDIRECT

    #define WHILE(pred, op, ...) \
        IF(pred(__VA_ARGS__)) \
        ( \
            OBSTRUCT(WHILE_INDIRECT) () \
            ( \
                pred, op, op(__VA_ARGS__) \
            ), \
            __VA_ARGS__ \
        )
    #define WHILE_INDIRECT() WHILE
    

    出于演示目的,我们将创建一个谓词,用于在参数数为1时进行检查:

    #define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
    #define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
    
    #define IS_1(x) CHECK(PRIMITIVE_CAT(IS_1_, x))
    #define IS_1_1 ~, 1,
    
    #define PRED(x, ...) COMPL(IS_1(NARGS(__VA_ARGS__)))
    

    接下来,我们创建一个操作符,我们将只包含两个令牌。我们还创建一个最终操作符(称为 M )这将处理最终输出:

    #define OP(x, y, ...) CAT(x, y), __VA_ARGS__ 
    #define M(...) CAT(__VA_ARGS__)
    

    宏:

    M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz
    

        2
  •  10
  •   CesarB    17 年前
        3
  •  9
  •   Boumbles Andru Luvisi    10 年前

    您可以使用递归包含文件。不幸的是,循环的迭代次数不能超过预处理器允许的最大深度。

    事实证明C++模板是图灵完整的,可以以类似的方式使用。退房 Generative Programming

        4
  •  6
  •   Robert Gould    17 年前

    我使用元模板编程就是为了这个目的,一旦你掌握了窍门,它就会很有趣。在谨慎使用时非常有用。因为正如前面提到的,它的图灵完成,甚至可以导致编译器进入无限循环,或者堆栈溢出!没有什么比去喝杯咖啡更能让你发现你的编译占用了超过30 GB的内存和所有的CPU来编译你的无限循环代码!

        5
  •  6
  •   Vlad    14 年前

    #ifdef pad_always
    
    #define pad(p,f) p##0
    
    #else
    
    #define pad0(p,not_used) p
    #define pad1(p,not_used) p##0
    
    #define pad(p,f) pad##f(p,)
    
    #endif
    
    // f - padding flag
    // p - prefix so far
    // a,b,c - digits
    // x - action to invoke
    
    #define n0(p,x)
    #define n1(p,x)         x(p##1)
    #define n2(p,x) n1(p,x) x(p##2)
    #define n3(p,x) n2(p,x) x(p##3)
    #define n4(p,x) n3(p,x) x(p##4)
    #define n5(p,x) n4(p,x) x(p##5)
    #define n6(p,x) n5(p,x) x(p##6)
    #define n7(p,x) n6(p,x) x(p##7)
    #define n8(p,x) n7(p,x) x(p##8)
    #define n9(p,x) n8(p,x) x(p##9)
    
    #define n00(f,p,a,x)                       n##a(pad(p,f),x)
    #define n10(f,p,a,x) n00(f,p,9,x) x(p##10) n##a(p##1,x)
    #define n20(f,p,a,x) n10(f,p,9,x) x(p##20) n##a(p##2,x)
    #define n30(f,p,a,x) n20(f,p,9,x) x(p##30) n##a(p##3,x)
    #define n40(f,p,a,x) n30(f,p,9,x) x(p##40) n##a(p##4,x)
    #define n50(f,p,a,x) n40(f,p,9,x) x(p##50) n##a(p##5,x)
    #define n60(f,p,a,x) n50(f,p,9,x) x(p##60) n##a(p##6,x)
    #define n70(f,p,a,x) n60(f,p,9,x) x(p##70) n##a(p##7,x)
    #define n80(f,p,a,x) n70(f,p,9,x) x(p##80) n##a(p##8,x)
    #define n90(f,p,a,x) n80(f,p,9,x) x(p##90) n##a(p##9,x)
    
    #define n000(f,p,a,b,x)                           n##a##0(f,pad(p,f),b,x)
    #define n100(f,p,a,b,x) n000(f,p,9,9,x) x(p##100) n##a##0(1,p##1,b,x)
    #define n200(f,p,a,b,x) n100(f,p,9,9,x) x(p##200) n##a##0(1,p##2,b,x)
    #define n300(f,p,a,b,x) n200(f,p,9,9,x) x(p##300) n##a##0(1,p##3,b,x)
    #define n400(f,p,a,b,x) n300(f,p,9,9,x) x(p##400) n##a##0(1,p##4,b,x)
    #define n500(f,p,a,b,x) n400(f,p,9,9,x) x(p##500) n##a##0(1,p##5,b,x)
    #define n600(f,p,a,b,x) n500(f,p,9,9,x) x(p##600) n##a##0(1,p##6,b,x)
    #define n700(f,p,a,b,x) n600(f,p,9,9,x) x(p##700) n##a##0(1,p##7,b,x)
    #define n800(f,p,a,b,x) n700(f,p,9,9,x) x(p##800) n##a##0(1,p##8,b,x)
    #define n900(f,p,a,b,x) n800(f,p,9,9,x) x(p##900) n##a##0(1,p##9,b,x)
    
    #define n0000(f,p,a,b,c,x)                               n##a##00(f,pad(p,f),b,c,x)
    #define n1000(f,p,a,b,c,x) n0000(f,p,9,9,9,x) x(p##1000) n##a##00(1,p##1,b,c,x)
    #define n2000(f,p,a,b,c,x) n1000(f,p,9,9,9,x) x(p##2000) n##a##00(1,p##2,b,c,x)
    #define n3000(f,p,a,b,c,x) n2000(f,p,9,9,9,x) x(p##3000) n##a##00(1,p##3,b,c,x)
    #define n4000(f,p,a,b,c,x) n3000(f,p,9,9,9,x) x(p##4000) n##a##00(1,p##4,b,c,x)
    #define n5000(f,p,a,b,c,x) n4000(f,p,9,9,9,x) x(p##5000) n##a##00(1,p##5,b,c,x)
    #define n6000(f,p,a,b,c,x) n5000(f,p,9,9,9,x) x(p##6000) n##a##00(1,p##6,b,c,x)
    #define n7000(f,p,a,b,c,x) n6000(f,p,9,9,9,x) x(p##7000) n##a##00(1,p##7,b,c,x)
    #define n8000(f,p,a,b,c,x) n7000(f,p,9,9,9,x) x(p##8000) n##a##00(1,p##8,b,c,x)
    #define n9000(f,p,a,b,c,x) n8000(f,p,9,9,9,x) x(p##9000) n##a##00(1,p##9,b,c,x)
    
    #define n00000(f,p,a,b,c,d,x)                                   n##a##000(f,pad(p,f),b,c,d,x)
    #define n10000(f,p,a,b,c,d,x) n00000(f,p,9,9,9,9,x) x(p##10000) n##a##000(1,p##1,b,c,d,x)
    #define n20000(f,p,a,b,c,d,x) n10000(f,p,9,9,9,9,x) x(p##20000) n##a##000(1,p##2,b,c,d,x)
    #define n30000(f,p,a,b,c,d,x) n20000(f,p,9,9,9,9,x) x(p##30000) n##a##000(1,p##3,b,c,d,x)
    #define n40000(f,p,a,b,c,d,x) n30000(f,p,9,9,9,9,x) x(p##40000) n##a##000(1,p##4,b,c,d,x)
    #define n50000(f,p,a,b,c,d,x) n40000(f,p,9,9,9,9,x) x(p##50000) n##a##000(1,p##5,b,c,d,x)
    #define n60000(f,p,a,b,c,d,x) n50000(f,p,9,9,9,9,x) x(p##60000) n##a##000(1,p##6,b,c,d,x)
    #define n70000(f,p,a,b,c,d,x) n60000(f,p,9,9,9,9,x) x(p##70000) n##a##000(1,p##7,b,c,d,x)
    #define n80000(f,p,a,b,c,d,x) n70000(f,p,9,9,9,9,x) x(p##80000) n##a##000(1,p##8,b,c,d,x)
    #define n90000(f,p,a,b,c,d,x) n80000(f,p,9,9,9,9,x) x(p##90000) n##a##000(1,p##9,b,c,d,x)
    
    #define cycle5(c1,c2,c3,c4,c5,x) n##c1##0000(0,,c2,c3,c4,c5,x)
    #define cycle4(c1,c2,c3,c4,x) n##c1##000(0,,c2,c3,c4,x)
    #define cycle3(c1,c2,c3,x) n##c1##00(0,,c2,c3,x)
    #define cycle2(c1,c2,x) n##c1##0(0,,c2,x)
    #define cycle1(c1,x) n##c1(,x)
    
    #define concat(a,b,c) a##b##c
    
    #define ck(arg) a[concat(,arg,-1)]++;
    #define SIZEOF(x) (sizeof(x) / sizeof((x)[0]))
    
    void check5(void)
    {
        int i, a[32769];
    
        for (i = 0; i < SIZEOF(a); i++) a[i]=0;
    
        cycle5(3,2,7,6,9,ck);
    
        for (i = 0; i < SIZEOF(a); i++) if (a[i] != 1) printf("5: [%d] = %d\n", i+1, a[i]);
    }
    
        6
  •  5
  •   Windows programmer    17 年前

    这里有一个滥用的规则,可以让它合法地完成。编写自己的C预处理器。让它以你想要的方式解释一些布拉格指令。

        7
  •  2
  •   Mikael    15 年前

    我发现,当编译器脾气暴躁,无法为我展开某些循环时,这个方案很有用

    #定义重复20(x){x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;}