Lorsqu'un pragma OpenMP est utilisé dans le cadre d'un argument pour une macro, il est incorrectement substitué. Dans ce 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 );
    }
  )
}

Je m'attendrais à ce qu'il soit étendu à:

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

Cependant, (selon gcc -E), il devient:

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

Ce comportement est-il correct? Comment puis-je obtenir le comportement attendu, de préférence sans modifier les arguments de la macro? Cela se produit-il avec tous les pragmas? Est-ce un effet de la macro variadique? Les autres compilateurs effectuent-ils la même substitution?

Utilisation de gcc (Ubuntu 5.4.0-6ubuntu1 ~ 16.04.10) 5.4.0 20160609

2
memorableUserNameHere 21 nov. 2018 à 01:21

3 réponses

Meilleure réponse

Ce comportement est-il correct?

Il serait plus exact de dire que ce comportement n'est pas incorrect . La norme dit ceci à propos des macro-arguments:

S'il existe des séquences de jetons de prétraitement dans la liste d'arguments qui agiraient autrement comme des directives de prétraitement, le comportement n'est pas défini.

Vous avez exercé ce cas, et en conséquence récolté un comportement indéfini.

Cela se produit-il avec tous les pragmas? Est-ce un effet de la macro variadique? Les autres compilateurs effectuent-ils la même substitution?

Le comportement n'est pas défini, il peut donc varier entre les implémentations conformes, et même au sein d'une même implémentation, pour une raison quelconque ou aucune. Je suppose que GCC est relativement cohérent dans ce domaine, mais vous ne devriez en aucun cas vous y fier. Il n'est pas spécifiquement lié au fait que la macro impliquée est variadique.

Comment puis-je obtenir le comportement attendu, de préférence sans modifier les arguments de la macro?

C11, qui est supporté par GCC 5, a une solution pour le cas spécifique des macros qui émettent des pragmas: l '_Pragma opérateur. Il est plus souvent utilisé dans le texte de remplacement de macro littéral, afin que vous puissiez avoir une macro qui représente un pragma, mais cela devrait également fonctionner dans un argument de macro. Vous devrez cependant modifier un peu l'argument de la macro:

  make_body(
    _Pragma("omp parallel for")
    for( int i = 0; i < 10; i += 1 ){
      foo( i );
    }
  )
1
John Bollinger 20 nov. 2018 à 22:41

Solution alternative, cela corrige un cas que nous avons vu dans du code réel qui est bien trop gros pour être recréé ici (pas que nous ne sachions exactement pourquoi cela se produit de toute façon). -fopenmp est également nécessaire.

#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 peut être utilisé pour placer correctement un pragma lorsque le code est passé à une autre macro comme argument Par exemple:

#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 peut être utilisé pour placer correctement un pragma lorsque le code fait simplement partie du corps d'une macro, et non comme entrée d'une autre macro Par exemple:

#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); )
0
memorableUserNameHere 26 juil. 2019 à 21:49

Vous n'êtes pas autorisé à placer une directive de prétraitement dans les arguments d'une macro de type fonction. (C11 6.10.3p11, dernière phrase.)

Dans ce cas, vous devriez être capable d’écrire _Pragma("omp parallel for") et d’obtenir l’effet souhaité. L'expérimentation indique que cela fonctionne correctement avec clang et gcc (versions 7 et 8 respectivement) mais seulement si vous spécifiez -fopenmp sur la ligne de commande. Oui, même si vous utilisez -E.

1
zwol 20 nov. 2018 à 22:39