【问题标题】:Incorrect Substitution of OpenMP Pragma in Macro Expansion宏扩展中 OpenMP Pragma 的错误替换
【发布时间】:2018-11-20 22:21:11
【问题描述】:

当 OpenMP pragma 用作宏参数的一部分时,它会被错误地替换。 在这段代码中:

#define make_body( ... ) { __VA_ARGS__ }
extern foo( int );
int main(){
  make_body(
    #pragma omp parallel for
    for( int i = 0; i < 10; i += 1 ){
      foo( i );
    }
  )
}

我希望它会扩展到:

extern foo( int )
int main(){
  {
    #pragma omp parallel for
    for( int i = 0; i < 10; i += 1 ){
      foo( i );
    }
  }
}

但是,(根据 gcc -E)它被扩展为:

extern foo( int );
int main(){
#pragma omp parallel for
  { 
    for( int i = 0; i < 10; i += 1 ){ 
      foo( i ); 
    } 
  }
}

这是正确的行为吗? 如何获得预期的行为,最好不更改宏的参数? 所有的编译指示都会发生这种情况吗? 这是可变参数宏的效果吗? 其他编译器是否执行相同的替换?

使用 gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609

【问题讨论】:

  • 我很好奇:真正的目标用例是什么?问题中给出的例子不可能是这样,因为像正常人一样输入文字{}会更简单,更省事。
  • 真正的用例是一个代码计时库,它在代码段周围插入计时器停止、启动和经过时间报告代码。到目前为止,它运行良好,但不幸的是这个 OpenMP 示例破坏了一些东西。为了解决这个问题,我将为这些单独的组件添加一些宏,因此代码部分不是宏参数。

标签: c gcc macros openmp variadic-macros


【解决方案1】:

这是正确的行为吗?

最准确的说法是不是不正确的行为。该标准对宏参数有这样的说法:

如果参数列表中存在预处理标记序列,否则它们将充当预处理指令,则行为未定义。

您已经执行了该案例,并因此获得了未定义的行为。

所有编译指示都会发生这种情况吗?这是可变参数宏的效果吗?其他编译器是否执行相同的替换?

该行为是未定义的,因此它可能会在符合标准的实现之间有所不同,甚至在相同的实现中,无论出于何种原因或没有。我猜 GCC 在这方面是相对一致的,但你绝不应该依赖它。它与可变参数所涉及的宏没有特别关联。

如何获得预期的行为,最好不更改宏的参数?

受 GCC 5 支持的 C11 具有针对发出编译指示的宏的特定情况的解决方案:the _Pragma operator。它更常用于文字宏替换文本,因此您可以拥有一个表示编译指示的宏,但它也应该在宏参数中工作。但是,您必须稍微更改宏参数:

  make_body(
    _Pragma("omp parallel for")
    for( int i = 0; i < 10; i += 1 ){
      foo( i );
    }
  )

【讨论】:

  • 嘘未定义的行为!感谢您的详细解释。
【解决方案2】:

您不能将任何预处理指令放在类似函数的宏的参数中。 (C116.10.3p11,最后一句。)

在这种情况下,你应该可以写_Pragma("omp parallel for"),得到你想要的效果。实验表明,这对 clang 和 gcc(分别为版本 7 和 8)都可以正常工作,但仅当您在命令行上指定 -fopenmp。是的,即使使用-E

【讨论】:

【解决方案3】:

替代解决方案修复了我们在实际代码中看到的一个案例,该案例非常大,需要在此处重新创建(并不是说我们完全确定它为什么会发生)。 -fopenmp也是必须的。

#define EMPTY()
#define DELAY(x) x EMPTY()
#define STRINGIZE(x) STRINGIZE_NO_PREPROCESS(x)
#define STRINGIZE_NO_PREPROCESS(x) #x

#define PRAGMA(x) _Pragma( STRINGIZE(x) )
#define DELAYED_PRAGMA_assist(x) PRAGMA(x)
#define DELAYED_PRAGMA DELAY(DELAYED_PRAGMA_assist)

#define PRAGMA_IN_MACRO_ARG(x) DELAYED_PRAGMA(x)
#define PRAGMA_IN_MACRO_BODY(x) PRAGMA(x)

PRAGMA_IN_MACRO_ARG 可用于在将代码作为参数传递给另一个宏时正确放置 pragma 例如:

#define WRAP( body ) { body }

#define loop( i, N, body )\
if( N > 0 ) \
WRAP( \
  PRAGMA_IN_MACRO_ARG(omp parallel for private(i)) \
  for( i = 0; i < N; ++i ){ \
      body \
  } \
)

loop( i, 10, printf("%d\n", i); )

PRAGMA_IN_MACRO_BODY 可用于在代码只是宏主体的一部分时正确放置编译指示,而不是作为另一个宏的输入 例如:

#define loop( i, N, body )\
if( N > 0 ) \
{\
  PRAGMA_IN_MACRO_BODY(omp parallel for private(i)) \
  for( i = 0; i < N; ++i ){ \
      body \
  } \
}

loop( i, 10, printf("%d\n", i); )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-03
    相关资源
    最近更新 更多