Search code examples
cgccmacrosopenmpvariadic-macros

Incorrect Substitution of OpenMP Pragma in Macro Expansion


When an OpenMP pragma is used as part of an argument for a macro, it gets incorrectly substituted. In this code:

#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 );
    }
  )
}

I would expect that it would be expanded to:

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

However, (according to gcc -E) it becomes expanded to:

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

Is this correct behavior? How can I get the expected behavior, preferably without changing the arguments to the macro? Does this happen with all pragmas? Is this an effect of the variadic macro? Do other compilers perform the same substitution?

Using gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609


Solution

  • Is this correct behavior?

    It would be most accurate to say that it is not incorrect behavior. The standard has this to say about macro arguments:

    If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, the behavior is undefined.

    You have exercised that case, and accordingly reaped undefined behavior.

    Does this happen with all pragmas? Is this an effect of the variadic macro? Do other compilers perform the same substitution?

    The behavior is undefined, so it may vary among conforming implementations, and even within the same implementation, for any reason or none. I would guess that GCC is relatively consistent in this area, but in no way should you rely on that. It is not specifically tied to the macro involved being variadic.

    How can I get the expected behavior, preferably without changing the arguments to the macro?

    C11, which is supported by GCC 5, has a solution for the specific case of macros that emit pragmas: the _Pragma operator. It is more often used in literal macro replacement text, so that you can have a macro that represents a pragma, but it ought to work in a macro argument, too. You will, however, have to change the macro argument a bit:

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