在C ++中使用具有不同数量参数的嵌套宏

时间:2017-05-31 05:41:35

标签: c++ c++11 macros c-preprocessor

以下代码在g ++ -std = c ++ 11编译器的编译中失败。

    # include<iostream>
    # include<vector>

    using namespace std;


    # define stlf(x)        x.begin(), x.end()
    # define repf(it, a, b) for(auto it = a ; it != b ; ++it)


    /*
    // Also, following alternative fails

    # define repf(it, a, b) for(auto it = a ; it != b ; ++it)
    # define stlf(x)        x.begin(), x.end()

    */



    typedef vector<int > vi;

    # define pd(x)  printf("%d", x);

    int main(void){

        vi arr(10, -1);

        repf(arr, stlf(arr))
            pd(arr[i]);


        return 0;
    }

1。为什么会这样?

2。 C ++预处理器实现者的实现问题是什么,他们避免了这个功能?

3. 我怎样才能使用这样的快捷方式?

4 个答案:

答案 0 :(得分:5)

你的两个选择是相同的。定义宏的顺序与它们的扩展无关;它只与扩张时的定义有关。

为什么会这样?

您使用两个参数调用宏repf,但它需要三个参数。这是一个错误,简单明了,因此预处理失败。

C ++预处理器实现者的实现问题是什么,他们避免了这个功能?

我认为你在这里做出了无根据的假设。问题不在于预处理器&#34;缺乏&#34;一些&#34;功能&#34 ;;你对预处理器如何工作的期望是错误的。

据推测,您希望预处理器执行以下操作:

  1. repf(arr, stlf(arr))
  2. repf(arr, arr.begin(), arr.end())
  3. for(auto it = arr.begin() ; it != arr.end() ; ++it)
  4. ...从第1步到第2步,stlf(arr)得到扩展;然后将其扩展放入repf的调用中,然后在第3步扩展。

    问题是,这不是预处理器的工作方式。鉴于示例已被破坏,我无法正确说明这些步骤,因此我们假设我们这样做是为了说明目的:

    #define FOO(X, Y) BAR(X, Y)
    #define BAR(X,Y,Z) x is X y is Y z is Z
    #define ACOMMAB A, B
    FOO(ACOMMAB, C)
    

    最后一行扩展为x is A y is B c is Z,它更像是这样:

    1. FOO(ACOMMAB, C)
    2. BAR(ACOMMAB, C)
    3. BAR(A, B, C)
    4. x is A y is B c is Z
    5. 请注意,内部宏不会先扩展;相反,外部宏确实如此。另请注意,此示例注入逗号;所以注入一个逗号肯定是你可以实际做的事情,我认为这是&#34;功能&#34;你提到这是避免的。

      我怎样才能使用这样的快捷方式?

      鉴于预处理器并不像您认为的那样工作,您可能不想使用它来执行您认为要用它做的事情......即使是速度编码也是如此。 stlf很乐意为函数调用构建两个参数,但宏不是函数。似乎是你最好的选择。

答案 1 :(得分:2)

问题是,正如H Walters所指出的那样, order 中的宏做事情

  • 从输入令牌流
  • 读取(解析)参数
  • 在这些参数中展开宏(除非它们涉及#或##运算符)
  • 将那些(扩展的)参数替换为宏的主体
  • 重新扫描正文,以便其他宏展开。

请注意,上面有两个地方可以展开其他宏,因此虽然订单可能对您想要做的事情有误,但您可以通过添加额外的宏调用来实现目标。发生在你想要的时候。例如,如果您有一个宏

#define expand(...)   __VA_ARGS__

什么都不做(只需要一个或多个参数并在其中扩展宏),您可以使用它在正确的位置获得额外的宏扩展:

expand( repf expand((arr, stlf(arr))) )

将展开到您想要的for(auto it = arr.begin() ; it != arr.end() ; ++it)

这可能看起来很笨拙,但你可以将调用包装在另一个重新聚焦的宏中:

# define stlf(x)        x.begin(), x.end()
# define expand(...) __VA_ARGS__
# define repf(...) expand(repf2 expand((__VA_ARGS__)))
# define repf2(it, a, b) for(auto it = a ; it != b ; ++it)

现在repf(arr, stlf(arr))扩展了你想要的方式。

答案 2 :(得分:1)

repf期待3个参数而你只传递2.你不能使用另一个宏的扩展结果(stlf)作为参数(对于另一个宏)。预处理器/编译器不是这样设计的。

我不反对宏(在许多情况下它们非常有用),但对于这个简单的情况,你不能使用宏。它使您的程序难以阅读,维护和调试。避免!

理想情况下,你应该使用基于范围的循环(没有宏):

for(int a : arr) ...

答案 3 :(得分:0)

它应该是这样的:

define repf(it, a) for(auto it = std::begin(a) ; it != std::end(a) ; ++it)

但是你为什么要使用宏呢?