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

变量递归预处理器宏-有可能吗?

  •  39
  • Christoffer  · 技术社区  · 15 年前

    我遇到了一个理论上的小问题。在我维护的一段代码中,有一组宏

    #define MAX_OF_2(a, b)       (a) > (b) ? (a) : (b)
    #define MAX_OF_3(a, b, c)    MAX_OF_2(MAX_OF_2(a, b), c)
    #define MAX_OF_4(a, b, c, d) MAX_OF_2(MAX_OF_3(a, b, c), d)
    ...etc up to MAX_OF_8
    

    我想做的是用这样的东西代替它们:

    /* Base case #1, single input */
    #define MAX_OF_N(x)      (x)
    
    /* Base case #2, two inputs */
    #define MAX_OF_N(x, y)   (x) > (y) ? (x) : (y)
    
    /* Recursive definition, arbitrary number of inputs */
    #define MAX_OF_N(x, ...) MAX_OF_N(x, MAX_OF_N(__VA_ARGS__))
    

    …当然,这不是有效的预处理器代码。

    忽略这个特殊情况可能应该用函数而不是预处理器宏来解决 ,是否可以定义变量max_of_n()宏?

    为了清楚起见,最终结果应该是一个宏,它接受任意数量的参数并计算为其中最大的参数。我有一种奇怪的感觉,认为这应该是可能的,但我不知道如何。

    6 回复  |  直到 9 年前
        1
  •  9
  •   paxdiablo    15 年前

    不,因为预处理器只对文件进行一次“刷”。没有办法让它递归地定义宏。

    我看到的唯一一个这样做的代码是 变量,但使用了用户必须传递的默认值:

    x = MAX_OF_8 (a, b, -1, -1, -1, -1, -1, -1)
    

    假设所有值都是非负的。

    内联函数至少应该为C++提供相同的函数。如您所述,最好将其留给具有类似于 printf() .

        2
  •  35
  •   DS.    13 年前

    可以编写一个宏,该宏的计算结果为它调用的参数数。(我找不到指向我第一次看到它的地方的链接。)这样您就可以编写max_of_n(),它可以按您的意愿工作,但是您仍然需要所有编号的宏,直到达到某个限制:

    #define MAX_OF_1(a)         (a)         
    #define MAX_OF_2(a,b)       max(a, b)
    
    #define MAX_OF_3(a,...)    MAX_OF_2(a,MAX_OF_2(__VA_ARGS__))
    #define MAX_OF_4(a,...)    MAX_OF_2(a,MAX_OF_3(__VA_ARGS__))
    #define MAX_OF_5(a,...)    MAX_OF_2(a,MAX_OF_4(__VA_ARGS__))
    ...
    #define MAX_OF_64(a,...)   MAX_OF_2(a,MAX_OF_63(__VA_ARGS__))
    
    // NUM_ARGS(...) evaluates to the literal number of the passed-in arguments.
    #define _NUM_ARGS2(X,X64,X63,X62,X61,X60,X59,X58,X57,X56,X55,X54,X53,X52,X51,X50,X49,X48,X47,X46,X45,X44,X43,X42,X41,X40,X39,X38,X37,X36,X35,X34,X33,X32,X31,X30,X29,X28,X27,X26,X25,X24,X23,X22,X21,X20,X19,X18,X17,X16,X15,X14,X13,X12,X11,X10,X9,X8,X7,X6,X5,X4,X3,X2,X1,N,...) N
    #define NUM_ARGS(...) _NUM_ARGS2(0, __VA_ARGS__ ,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
    
    #define _MAX_OF_N3(N, ...) MAX_OF_ ## N(__VA_ARGS__)
    #define _MAX_OF_N2(N, ...) _MAX_OF_N3(N, __VA_ARGS__)
    #define MAX_OF_N(...)      _MAX_OF_N2(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
    

    现在 MAX_OF_N(a,b,c,d,e) 将评估为 max(a, max(b, max(c, max(d, e)))) . (我在GCC4.2.1上测试过。)

    请注意,基本情况( MAX_OF_2 )不会在扩展中多次重复它的参数(这就是为什么我将 max 在本例中)。否则,每个级别的扩展长度都将增加一倍,因此您可以想象64个参数会发生什么情况:)

        3
  •  10
  •   Steve Jessop    15 年前

    您可能会认为这种欺骗,因为它不是递归的,也不在预处理器中工作。它使用gcc扩展。它只适用于一种类型。但是,它是一个变量max_n宏:

    #include <iostream>
    #include <algorithm>
    
    #define MAX_OF_N(...) ({\
            int ra[] = { __VA_ARGS__ }; \
            *std::max_element(&ra[0], &ra[sizeof(ra)/sizeof(int)]); \
        })
    
    int main() {
        int i = 12;
        std::cout << MAX_OF_N(1,3,i,6);
    }
    

    哦,是的,因为初始化列表中的变量表达式,我认为不等价于此(使用它自己的函数来避免STD::Max元素)将在C89中工作。但我也不确定可变宏是否在c89中。

    我认为这里有一些东西可以绕过“只有一种类型”的限制。不过有点毛茸茸的:

    #include <iostream>
    #include <algorithm>
    
    #define MAX_OF_N(x, ...) ({\
            typeof(x) ra[] = { (x), __VA_ARGS__ }; \
            *std::max_element(&ra[0], &ra[sizeof(ra)/sizeof(ra[0])]); \
        })
    
    int main() {
        int i = 12;
        std::cout << MAX_OF_N(i+1,1,3,6,i);
    }
    
        4
  •  6
  •   fortran    15 年前

    我认为,即使您可以递归地扩展宏,您的方法在效率方面也会有一个小问题…当宏展开时,如果 MAX_OF_[N-1] 更大,那么你必须从头开始重新评估它。

    这是一个愚蠢愚蠢的答案,也许没有人会喜欢xd。

    文件“source.c”

    #include "my_macros.h"
    ...
    

    文件“makefile”

    myprogram: source.c my_macros.h
     gcc source.c -o myprogram
    
    my_macros.h: make_macros.py
     python make_macros.py > my_macros.h
    

    文件“make_macros.py”

    def split(l):
        n = len(l)
        return l[:n/2], l[n/2:]
    
    def gen_param_seq(n):
        return [chr(i + ord("A")) for i in range(n)]
    
    def make_max(a, b):
        if len(a) == 1:
            parta = "("+a[0]+")"
        else:
            parta = make_max(*split(a))
    
        if len(b) == 1:
            partb = "("+b[0]+")"
        else:
            partb = make_max(*split(b))
    
        return "("+parta +">"+partb+"?"+parta+":"+partb+")"
    
    for i in range(2, 9):
        p = gen_param_seq(i)
        print "#define MAX_"+str(i)+"("+", ".join(p)+") "+make_max(*split(p))
    

    然后定义这些漂亮的宏:

    #define MAX_2(A, B) ((A)>(B)?(A):(B))
    #define MAX_3(A, B, C) ((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))
    #define MAX_4(A, B, C, D) (((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D)))
    #define MAX_5(A, B, C, D, E) (((A)>(B)?(A):(B))>((C)>((D)>(E)?(D):(E))?(C):((D)>(E)?(D):(E)))?((A)>(B)?(A):(B)):((C)>((D)>(E)?(D):(E))?(C):((D)>(E)?(D):(E))))
    #define MAX_6(A, B, C, D, E, F) (((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))>((D)>((E)>(F)?(E):(F))?(D):((E)>(F)?(E):(F)))?((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))):((D)>((E)>(F)?(E):(F))?(D):((E)>(F)?(E):(F))))
    #define MAX_7(A, B, C, D, E, F, G) (((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))>(((D)>(E)?(D):(E))>((F)>(G)?(F):(G))?((D)>(E)?(D):(E)):((F)>(G)?(F):(G)))?((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))):(((D)>(E)?(D):(E))>((F)>(G)?(F):(G))?((D)>(E)?(D):(E)):((F)>(G)?(F):(G))))
    #define MAX_8(A, B, C, D, E, F, G, H) ((((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D)))>(((E)>(F)?(E):(F))>((G)>(H)?(G):(H))?((E)>(F)?(E):(F)):((G)>(H)?(G):(H)))?(((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D))):(((E)>(F)?(E):(F))>((G)>(H)?(G):(H))?((E)>(F)?(E):(F)):((G)>(H)?(G):(H))))
    

    最棒的是…它工作^ ^ ^

        5
  •  5
  •   Jason S    15 年前

    如果你在C++中沿着这条路走下去,看看 template metaprogramming . 它不漂亮,可能无法解决您的确切问题,但它将处理递归。

        6
  •  4
  •   Paul Fultz II    12 年前

    首先,宏不会重复展开。尽管如此,宏可以通过为每个递归级别创建宏,然后推导递归级别来实现重入。然而,所有这些重复和推导递归,都由 Boost.Preprocessor 图书馆。因此,可以使用高阶折叠宏计算最大值:

    #define MAX_EACH(s, x, y) BOOST_PP_IF(BOOST_PP_GREATER_EQUAL(x, y), x, y)
    #define MAX(...) BOOST_PP_SEQ_FOLD_LEFT(MAX_EACH, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) 
    
    MAX(3, 6, 8) //Outputs 8
    MAX(4, 5, 9, 2) //Outputs 9
    

    现在,这将理解0-256之间的文字数字。它不适用于C++变量或表达式,因为C预处理器不理解C++。它只是纯文本替换。但是C++提供了一个称为“函数”的特性,它将用于C++表达式,并且可以使用它来计算最大值。

    template<class T>
    T max(T x, T y)
    {
        return x > y ? x : y;
    }
    
    template<class X, class... T>
    auto max(X x, T ... args) -> decltype(max(x, max(args...)))
    {
        return max(x, max(args...));
    }
    

    现在,上面的代码确实需要一个C++ 11编译器。如果使用C++ 03,则可以创建函数的多个重载,以便模拟参数。此外,我们可以使用预处理器为我们生成这种重复的代码(这就是它的作用)。所以在C++ 03中,你可以这样写:

    template<class T>
    T max(T x, T y)
    {
        return x > y ? x : y;
    }
    
    #define MAX_FUNCTION(z, n, data) \
    template<class T> \
    T max(T x, BOOST_PP_ENUM_PARAMS(n, T x)) \
    { \
        return max(x, max(BOOST_PP_ENUM_PARAMS(n, x)));\
    }
    
    BOOST_PP_REPEAT_FROM_TO(2, 64, MAX_FUNCTION, ~)