当OpenMP编译指示用作宏参数的一部分时,它将被错误地替换。 在此代码中:
#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
答案 0 :(得分:1)
不允许将任何预处理指令放在类似函数的宏的参数中。 (C11 6.10.3p11,最后一句话。)
在这种情况下,您应该能够写_Pragma("omp parallel for")
并获得想要的效果。实验表明,这对clang和gcc(分别为版本7和8)都可以正常使用,但是仅当在命令行上指定-fopenmp
时才有效。是的,即使使用-E
。
答案 1 :(得分:1)
这是正确的行为吗?
最准确的说法是不正确行为。该标准对宏参数有这样的说法:
如果参数列表中有一些预处理标记序列,否则它们会充当预处理指令,则行为是不确定的。
您已经行使了该案,因此收获了不确定的行为。
这会发生在所有编译指示上吗?这是可变参数宏的效果吗?其他编译器是否执行相同的替换?
该行为是未定义的,因此出于任何原因或没有原因,在一致的实现之间,甚至在同一实现内,它可能也会有所不同。我猜想,海湾合作委员会在这方面是相对一致的,但您绝对不应依赖它。它并不与可变参数所涉及的宏特别相关。
GCC 5支持的如何获得预期的行为,最好不更改宏的参数?
C11提供了针对发出编译指示的宏的特定情况的解决方案:the _Pragma
operator。它更常用于文字宏替换文本中,因此您可以拥有一个表示杂注的宏,但是它也应该在宏参数中起作用。但是,您将不得不稍微更改宏参数:
make_body(
_Pragma("omp parallel for")
for( int i = 0; i < 10; i += 1 ){
foo( i );
}
)
答案 2 :(得分:0)
替代解决方案解决了我们在实际代码中看到的一个情况,这种情况在这里可以被重新创建(不是我们完全确定为什么还是会发生这种情况)。
Vcells
也很有必要。
-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
#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