【问题标题】:Can we implement a max or min macro, which can take variable arguments (more than two parameters )我们可以实现一个 max 或 min 宏,它可以接受可变参数(超过两个参数)
【发布时间】:2014-05-11 14:28:19
【问题描述】:

我想实现一个新的 max/min 宏,它可以带两个以上的参数,例如:

#define max( ... ) ...

然后,我可以这样使用它:

max( p0, p1, p2, p3 )
max( 2, 4, 100 )
max( 1,2,3,4,5,6,7 ) -> 7

如果这个宏可以帮助我们实现那个宏?

#define PP_EXPAND(X) X
#define PP_ARG_COUNT(...) PP_EXPAND(PP_ARG_POPER(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N

#define PP_ARG_AT(Index, ...) PP_ARG_AT_##Index(__VA_ARGS__)
#define PP_ARG_AT_0(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, __VA_ARGS__))
#define PP_ARG_AT_1(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, __VA_ARGS__))
#define PP_ARG_AT_2(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, __VA_ARGS__))
#define PP_ARG_AT_3(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, __VA_ARGS__))
#define PP_ARG_AT_4(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, __VA_ARGS__))
#define PP_ARG_AT_5(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, __VA_ARGS__))
#define PP_ARG_AT_6(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __VA_ARGS__))
#define PP_ARG_AT_7(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, __VA_ARGS__))
#define PP_ARG_AT_8(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, _8, __VA_ARGS__))
#define PP_ARG_AT_9(...)  PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, _7, __VA_ARGS__))
#define PP_ARG_AT_10(...) PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, _6, __VA_ARGS__))
#define PP_ARG_AT_11(...) PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, _5, __VA_ARGS__))
#define PP_ARG_AT_12(...) PP_EXPAND(PP_ARG_POPER(_1, _2, _3, _4, __VA_ARGS__))
#define PP_ARG_AT_13(...) PP_EXPAND(PP_ARG_POPER(_1, _2, _3, __VA_ARGS__))
#define PP_ARG_AT_14(...) PP_EXPAND(PP_ARG_POPER(_1, _2, __VA_ARGS__))
#define PP_ARG_AT_15(...) PP_EXPAND(PP_ARG_POPER(_1, __VA_ARGS__))
#define PP_ARG_AT_16(...) PP_EXPAND(PP_ARG_POPER( __VA_ARGS__))

【问题讨论】:

标签: c++ variadic-macros boost-preprocessor


【解决方案1】:

在 C++11 中,std::maxinitializer_list 一起使用,因此您可以使用

std::max({40, 31, 42, 13, 4, 25, 16, 27});

如果你真的想要MAX(p1, p2, p3) 语法,你可以这样做:

#define MAX(...) std::max({__VA_ARGS__})

【讨论】:

    【解决方案2】:

    您的问题包含了一半的答案 - 您可以使用 PP_ARG_COUNT 宏中显示的技术使用可变参数编号构建 min/max 宏(与原始代码一样,参数的数量将有限制,但你可以选择它)。

    这里是示例代码(最多 4 个参数):

    #include <stdio.h>
    
    #define __START(op, A, B, C, D, N, ...) __ARGS_##N(op, A, B, C, D)
    #define __ARGS_1(op, A, B, C, D) A
    #define __ARGS_2(op, A, B, C, D) __##op(A, B)
    #define __ARGS_3(op, A, B, C, D) __##op(A, __##op(B, C))
    #define __ARGS_4(op, A, B, C, D) __##op(__##op(A, B), __##op(C, D))
    
    
    #define __MIN(A, B) ((A) < (B) ? (A) : (B))
    #define __MAX(A, B) ((A) > (B) ? (A) : (B))
    
    #define min(...) __START(MIN, __VA_ARGS__, 4, 3, 2, 1)
    #define max(...) __START(MAX, __VA_ARGS__, 4, 3, 2, 1)
    
    int main(void)
    {
       printf("min(1) -> %d\n\n", min(1));
       printf("min(1.5, 2) -> %lf\n", min(1.5,2));
       printf("min(3, 2, 1.5) -> %lf\n", min(3,2,1.5));
       printf("min(1, 2, 3, 4) -> %d\n", min(1,2,3,4));
       printf("min(2, 3, 4, 1) -> %d\n\n", min(2,3,4,1));
    
       printf("max(2.5, 2.0) -> %lf\n", max(2.5, 2.0));
       printf("max(3, 2, 3.5) -> %lf\n", max(3, 2, 3.5));
       printf("max(1, 2, 3, 4) -> %d\n", max(1, 2, 3, 4));
       printf("max(2, 3, 4, 1) -> %d\n", max(2, 3, 4, 1));
    
       return 0;
    }
    

    如果你编译并运行程序,你会得到以下输出:

    min(1) -> 1
    
    min(1.5, 2) -> 1.500000
    min(3, 2, 1.5) -> 1.500000
    min(1, 2, 3, 4) -> 1
    min(2, 3, 4, 1) -> 1
    
    max(2.5, 2.0) -> 2.500000
    max(3, 2, 3.5) -> 3.500000
    max(1, 2, 3, 4) -> 4
    max(2, 3, 4, 1) -> 4
    

    它是如何工作的。 __START 宏接受以下参数:

    • op - 宏名(前面不带双下划线),只用两个参数进行必要的操作
    • ABCD - 这些参数将接收min/max 的参数,如果min/max 参数的数量较少,可能还有一些虚拟参数然后是最大值(此示例代码中为 4)。
    • N - min/max 参数的数量。
    • ... - 其他一些虚拟参数。

    __START 将扩展为 __ARGS_1..__ARGS_4 之一,具体取决于参数编号。参数编号通过添加4, 3, 2, 1参数调用__START宏获得:

    #define min(...) __START(MIN, __VA_ARGS__, 4, 3, 2, 1)
    

    因此,如果您调用例如 min(1.5, 2.5, 3.5),它将扩展为(我在 cmets 中添加了参数名称):

    __START(/*op=*/ MIN, /*A=*/ 1.5, /*B=*/ 2.5, /*C=*/ 3.5, /*D=*/ 4, /*N=*/ 3, 2, 1)
    

    __START 然后将扩展为 __ARGS_3,以下扩展是微不足道的。 现在可以清楚地看到参数编号是如何“计数”的以及它是如何工作的。您可以轻松实现具有其他功能的相同宏并增加最大参数数量,例如sum

    #define __SUM(A, B) ((A)+(B))
    #define sum(...) __START(SUM, __VA_ARGS__, 4, 3, 2, 1)
    

    认为它不如min/max 有用。

    附:如果您使用 Visual C++,您将需要添加一些解决方法(如第一篇文章中的 PP_EXPAND)来克服 VC++ 预处理器错误 see for details。我用的是 GCC,它不需要。

    【讨论】:

      【解决方案3】:

      有 C++ STL 算法可以做同样的事情:

      max_element.

      min_element

      开始使用这些而不是编写宏来实现这一点:

       int arr[] = {1,2,3,4,5};
       int* min = std::min_element(arr, arr+5);
       int* max = std::max_element(arr,arr+5);
       std::cout<<"min:"<<*min<<"max:"<<*max<<std::endl;
      

      【讨论】:

      • 你好,我不会使用这个代码,我问这个问题,因为我想研究这种预处理技术,非常感谢......
      • @Hikari:几乎不需要用纯C++编写宏。这些 STL 算法将与 MACRO 一样有效(几乎)。您也可以使用内联概念来实现 MACRO 类型的行为。所以你应该研究这些概念而不是 MACRO ..选择是你的 :)
      • @tmp 这仍然是一个有趣的学习练习。我们无法读懂光的想法。
      【解决方案4】:

      使用Boost.Preprocessor,可以这样实现:

      #define MAX_FOLD(s, state, elem) BOOST_PP_MAX(state, elem) 
      #define MAX(...) BOOST_PP_SEQ_FOLD_LEFT(MAX_FOLD, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) 
      

      由于预处理器不直接支持扩展时的比较,从头开始实现这一切需要大量的工作。使用技术here,您可以实现一个计数器和一个while 循环结构。有了它,您可以实现减法,这将允许您实现小于(或大于),这是MAX 所需要的。然后使用另一个while,您可以对可变参数进行折叠。

      最后,在预处理器中执行所有这些操作有一些限制。预处理器并不完全图灵完备。因此,在 boost 示例中,您将被限制为 0 到 256 之间的值(这完全是 boost 限制,如果您自己这样做,您可以提高该限制)。根据您想要实现的目标,最好为 max 编写一个可变参数函数:

      template<class T, class U>
      T max(T x, T y)
      {
          return x > y ? x : y;
      }
      
      template<class... Ts>
      T max(T x, Ts... xs)
      {
          return max(x, max(xs...)); 
      }
      

      【讨论】:

        猜你喜欢
        • 2014-07-11
        • 2018-04-20
        • 1970-01-01
        • 1970-01-01
        • 2010-10-20
        • 1970-01-01
        • 2011-08-08
        • 2021-12-26
        • 1970-01-01
        相关资源
        最近更新 更多