宏扩展中 OpenMP Pragma 的错误替换
Incorrect Substitution of OpenMP Pragma in Macro Expansion
当 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
不允许将任何预处理指令放入类函数宏的参数中。 (C116.10.3p11,最后一句。)
在这种情况下,您应该能够编写_Pragma("omp parallel for")
并获得您想要的效果。实验表明,这对 clang 和 gcc(分别为版本 7 和 8)都能正常工作,但 仅当 您在命令行上指定 -fopenmp
时。是的,即使使用 -E
.
Is this correct behavior?
最准确的说法是不正确行为。该标准对宏参数有这样的说法:
If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, the behavior is undefined.
你已经执行了那个案例,因此收获了未定义的行为。
Does this happen with all pragmas? Is this an effect of the variadic macro? Do other compilers perform the same substitution?
该行为是未定义的,因此它可能因任何原因或 none 在符合规范的实现之间有所不同,甚至在同一实现中也是如此。我猜想 GCC 在这方面是相对一致的,但你不应该依赖它。它与可变参数宏无关。
How can I get the expected behavior, preferably without changing the arguments to the macro?
GCC 5 支持的 C11 有一个针对发出编译指示的宏的特定情况的解决方案:the _Pragma
operator。它更常用于文字宏替换文本,这样您就可以拥有一个表示编译指示的宏,但它也应该在宏参数中起作用。但是,您必须稍微更改宏参数:
make_body(
_Pragma("omp parallel for")
for( int i = 0; i < 10; i += 1 ){
foo( i );
}
)
替代解决方案修复了我们在实际代码中看到的一个案例,该案例太大了,无法在此处重新创建(我们不确定为什么会发生这种情况)。
-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
可用于在代码只是宏主体的一部分而不是另一个宏的输入时正确放置 pragma
例如:
#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); )
当 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
不允许将任何预处理指令放入类函数宏的参数中。 (C116.10.3p11,最后一句。)
在这种情况下,您应该能够编写_Pragma("omp parallel for")
并获得您想要的效果。实验表明,这对 clang 和 gcc(分别为版本 7 和 8)都能正常工作,但 仅当 您在命令行上指定 -fopenmp
时。是的,即使使用 -E
.
Is this correct behavior?
最准确的说法是不正确行为。该标准对宏参数有这样的说法:
If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, the behavior is undefined.
你已经执行了那个案例,因此收获了未定义的行为。
Does this happen with all pragmas? Is this an effect of the variadic macro? Do other compilers perform the same substitution?
该行为是未定义的,因此它可能因任何原因或 none 在符合规范的实现之间有所不同,甚至在同一实现中也是如此。我猜想 GCC 在这方面是相对一致的,但你不应该依赖它。它与可变参数宏无关。
How can I get the expected behavior, preferably without changing the arguments to the macro?
GCC 5 支持的 C11 有一个针对发出编译指示的宏的特定情况的解决方案:the _Pragma
operator。它更常用于文字宏替换文本,这样您就可以拥有一个表示编译指示的宏,但它也应该在宏参数中起作用。但是,您必须稍微更改宏参数:
make_body(
_Pragma("omp parallel for")
for( int i = 0; i < 10; i += 1 ){
foo( i );
}
)
替代解决方案修复了我们在实际代码中看到的一个案例,该案例太大了,无法在此处重新创建(我们不确定为什么会发生这种情况)。
-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
可用于在代码只是宏主体的一部分而不是另一个宏的输入时正确放置 pragma
例如:
#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); )