Неверная замена OpenMP Pragma в макропроцессоре - PullRequest
0 голосов
/ 21 ноября 2018

Когда прагма 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

Ответы [ 3 ]

0 голосов
/ 21 ноября 2018

Это правильное поведение?

Было бы наиболее точно сказать, что это не неправильное поведение.Стандарт говорит об аргументах макросов:

Если в списке аргументов есть последовательности токенов предварительной обработки, которые в противном случае действовали бы как директивы предварительной обработки, поведение не определено.

Вы применили этот случай и, соответственно, пожали неопределенное поведение.

Это происходит со всеми прагмами?Это эффект вариационного макроса?Другие компиляторы выполняют ту же подстановку?

Поведение не определено, поэтому оно может варьироваться в зависимости от соответствующих реализаций, и даже в пределах одной и той же реализации, по любой причине или без таковой.Я предполагаю, что GCC относительно последовательна в этой области, но ни в коем случае не следует полагаться на это.Он не привязан конкретно к макросу, являющемуся переменным.

Как получить ожидаемое поведение, желательно без изменения аргументов макроса?

C11, которыйподдерживается GCC 5, имеет решение для конкретного случая макросов, которые испускают прагмы: оператор _Pragma .Он чаще используется в буквальном тексте замены макроса, поэтому у вас может быть макрос, представляющий прагму, но он также должен работать в аргументе макроса.Однако вам придется немного изменить аргумент макроса:

  make_body(
    _Pragma("omp parallel for")
    for( int i = 0; i < 10; i += 1 ){
      foo( i );
    }
  )
0 голосов
/ 27 июля 2019

Альтернативное решение, это исправляет случай, который мы видели в реальном коде, который слишком большой, чтобы воссоздать его здесь (не то, чтобы мы точно знали, почему это происходит в любом случае).-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 может использоваться для правильного размещения прагмы, когда код является просто частью тела макроса, а не как ввод в другой макрос. Например:

#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 голосов
/ 21 ноября 2018

Вам не разрешается помещать какие-либо директивы предварительной обработки внутри аргументов в функционально-подобный макрос.(C11 6.10.3p11 , последнее предложение.)

В этом случае вы должны иметь возможность написать _Pragma("omp parallel for") и получить желаемый эффект.Эксперимент показывает, что это правильно работает как с clang, так и с gcc (версии 7 и 8 соответственно), но только если , вы указываете -fopenmp в командной строке.Да, даже если используется -E.

...