我们可以实现max或min宏,它可以采用变量参数(超过两个参数)

时间:2014-04-01 01:42:05

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

我想实现一个新的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__))

4 个答案:

答案 0 :(得分:4)

在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__})

答案 1 :(得分:2)

有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;

答案 2 :(得分:2)

使用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,您可以对varidiac参数进行折叠。

最后,在预处理器中执行所有这些操作存在一些限制。预处理器并不完全完整。因此在boost示例中,您将被限制为0到256之间的值(这完全是一个提升限制,如果您自己动手,则可以提高该限制)。根据您想要实现的目标,最好为max:

编写varidiac函数
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...)); 
}

答案 3 :(得分:1)

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

以下是示例代码(最多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),它将扩展为(我在注释中添加了参数名称):

__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有用。

P.S。如果使用Visual C ++,则需要添加一些解决方法(例如第一篇文章中的PP_EXPAND)以克服VC ++预处理程序错误see for details。我使用了GCC,不需要它。