Макрос Stringify с GNU gfortran

Как я могу преобразовать макрос препроцессора в строку с помощью GNU gfortran? Я хотел бы передать определение макроса в GNU gfortran, которое затем будет использоваться как строка в коде.

Фактически я хотел бы сделать это:

program test
implicit none
character (len=:), allocatable :: astring
astring = MYMACRO
write (*, *) astring
end program test

а затем построить с помощью:

gfortran -DMYMACRO=hello test.F90

Я пробовал создавать различные макросы, например:

#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
...
astring = STRINGIFY(MYMACRO)

но это не работает с препроцессором gfortran.

Я также пытался использовать другой стиль макроса:

#define STRINGIFY(x) "x"
...
astring = STRINGIFY(MYMACRO)

но это просто создает строку, содержащую текст «MYMACRO».

Затем я попытался изменить определение макроса на:

-DMYMACRO=\"hello\"

но это вызвало несвязанную проблему в процессе сборки.

спасибо за помощь


person Jim Eliot    schedule 27.07.2015    source источник
comment
-DMYMACRO=\"hello\" правильный подход. Какие проблемы это вызвало?   -  person Vladimir F    schedule 27.07.2015
comment
@VladimirF Экранирование кавычек отлично работает в моем примере кода, но в реальном проекте некоторые сложности процесса сборки приводят к его сбою. Изменение процедуры сборки на самом деле не вариант, поэтому я хотел бы найти альтернативный метод, если это возможно.   -  person Jim Eliot    schedule 27.07.2015
comment
Не могли бы вы уточнить, почему экранирование кавычек не работает в реальном проекте? Трудно предоставить решения с неизвестным требованием.   -  person Ross    schedule 28.07.2015
comment
@Ross В другом месте процесса сборки все флаги компилятора заключены в кавычки и включены в исходный файл. Добавление \ вокруг определения макроса приводит к сбою компилятора.   -  person Jim Eliot    schedule 28.07.2015
comment
@VladimirF Я обновил вопрос, включив в него #define STRINGIFY(x) "x". Я обнаружил, что он только что создал строку, содержащую текст «MYMACRO».   -  person Jim Eliot    schedule 28.07.2015
comment
ОК, наконец-то я понял, в чем отличие от вопроса, который я связал ранее. К сожалению, я не знаю решения.   -  person Vladimir F    schedule 28.07.2015


Ответы (3)


Ваша попытка использовать хорошо известный рецепт стробирования препроцессора C, а именно:

#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)

терпит неудачу по двум причинам, каждая из которых достаточна сама по себе.

Первое и самое простое: исходный файл, в котором вы пытаетесь его использовать, очевидно, имеет расширение .f90. Что это расширение означает для gfortran (и для драйвера компилятора GCC под любым другим именем): Исходный код свободной формы Fortran, который не должен подвергаться предварительной обработке. Аналогично .f95, .f03 и .f08. Если вы хотите, чтобы gfortran выводил, что исходный файл содержит код Fortran в свободной форме, который должен быть предварительно обработан, присвойте ему одно из расширений .F90, .F95, .F03 или .F08. См. документацию GCC по этим точкам.

Однако, даже если вы делаете эту простую вещь, вторая причина кусается.

Использование препроцессора C для предварительной обработки исходного кода Fortran так же старо, как C (который хотя и стар, но намного моложе, чем Fortran). gfortran обязуется не нарушать древний рабочий код; поэтому, когда он вызывает препроцессор C, он вызывает его в традиционном режиме. Традиционный режим препроцессора C — это то, как препроцессор вел себя до первой стандартизации языка C (1989 г.), поскольку это нестандартное поведение может быть зафиксировано. В традиционном режиме препроцессор не распознает строковый оператор '#', который был введен в первом стандарте C. Вы можете проверить это, вызвав препроцессор напрямую, например:

cpp -traditional test.c

где test.c содержит некоторую попытку использовать рецепт стрингификации. Попытка не удалась.

Вы не можете уговорить gfortran самостоятельно работать по рецепту стрингификации.

Но есть обходной путь. Вы можете вызвать cpp напрямую, не обремененный традиционным режимом, для предварительной обработки исходного кода на Фортране, в котором вы хотите выполнить строковое преобразование, и передать его вывод в gfortran. Если вы уже знаете это и ищете gfortran-одиночное решение, вам не нужно читать дальше.

Выполнение стробирования в вашем тестовом источнике таким образом будет выглядеть так:

cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' '-DMYMACRO=STRINGIFY(hello)' test.f90

Результат этого:

# 1 "test.f90"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.f90"
program test
implicit none
character (len=:), allocatable :: astring
astring = "hello"
write (*, *) astring
end program test

И этот вывод - это то, что вы хотите скомпилировать. Вы также можете сделать это:

cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' test.f90 > /tmp/test.f90 \
&& gfortran -o test /tmp/test.f90

Затем вы обнаружите, что ./test существует и что его выполнение выводит hello.

Вы можете удалить промежуточный временный файл с дальнейшей доработкой. Ваш исходный код F90 будет скомпилирован как F95, поскольку последний является консервативным по отношению к первому. Таким образом, вы можете воспользоваться тем фактом, что GCC будет компилировать исходный код, передаваемый на его стандартный ввод, если вы укажете ему, какой язык вы передаете, используя параметр -x language. Диалекты Фортрана, которые вы можете указать таким образом, это f77, f77-cpp-input, f95 и f95-cpp-input, где префикс -cpp-input означает, что исходный код должен быть предварительно обработан, а его отсутствие означает, что это не так. Таким образом

cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' test.f90 |  gfortran -x f95 -o test -

работает так же, как и предыдущее решение, за исключением временного файла, и выдает безобидное предупреждение:

Warning: Reading file '<stdin>' as free form

(Обратите внимание и сохраните последний - в командной строке. Это то, что говорит gfortran скомпилировать стандартный ввод.). Значение -x f95 приносит дополнительную экономию, поскольку исходный код, предварительно обработанный cpp, не подвергается повторной предварительной обработке компилятором.

Использование опции -std=c89 при вызове cpp требует осторожного объяснения. Это приводит к тому, что cpp соответствует самому раннему стандарту C. Это максимально близко к -traditional, насколько мы можем получить, все еще используя #-оператор, от которого зависит рецепт строковой обработки. способ; в противном случае сам gfortran не будет применять -traditional. В случае с вашей тестовой программой вы можете безопасно опустить -std=c89, что позволит cpp соответствовать стандарту C по умолчанию, когда он был построен. Но если вы разрешите или укажете, чтобы он соответствовал -std=c99 или более поздней версии, то стандарт предписывает распознавать // как начало однострочного комментария (согласно C++), благодаря чему любая строка Фортрана, содержащая оператор конкатенации, будет быть усеченным при первом появлении.

Естественно, если вы используете make или другую систему сборки для сборки кода, в котором вам нужны строковые макросы, у вас будет способ сообщить системе сборки, какие действия составляют компиляцию данного класса компилируемых файлы. Для любого исходного файла Fortran fsrc, который вы хотите скомпилировать с преамбулой строковой модификации, действия, которые необходимо указать, будут примерно такими:

cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' fsrc.f90 | gfortran -x f95 -c -o fsrc.o -
person Mike Kinghan    schedule 31.07.2015
comment
Спасибо, что написали такой развернутый ответ. В ответ на ваш первый вопрос, это была просто опечатка с моей стороны - я использовал суффикс .F90, а не .f90. Я отредактировал вопрос, чтобы отразить это. Во-вторых, вы ответили на мой вопрос: «Вы не можете уговорить gfortran самостоятельно работать по рецепту стрингификации». Похоже, мне придется либо найти другой способ сделать то, что я хочу, либо убедить владельца проекта изменить процедуру сборки. Остальная часть вашего второго ответа дает мне решение, которое я могу предложить. Спасибо еще раз. - person Jim Eliot; 04.08.2015

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

astring = "&
&MYMACRO"

Предостережение заключается в том, что это действительно работает только с традиционным препроцессором и, например, не работает с компилятором Intel ifort, который слишком умен, чтобы попасться на этот трюк. Мое текущее решение состоит в том, чтобы определить отдельные макросы стробирования для gfortran как:

#ifdef __GFORTRAN__
# define STRINGIFY_START(X) "&
# define STRINGIFY_END(X) &X"
#else /* default stringification */
# define STRINGIFY_(X) #X
# define STRINGIFY_START(X) &
# define STRINGIFY_END(X) STRINGIFY_(X)
#endif

program test
implicit none
character (len=:), allocatable :: astring
astring = STRINGIFY_START(MYMACRO)
STRINGIFY_END(MYMACRO)
write (*, *) astring
end program test

Выглядит очень некрасиво, но со своей задачей справляется.

person none_00    schedule 21.09.2017

Удаление кавычек вокруг x в STRINGIFY решило проблему для меня:

#define STRINGIFY(x) x   ! No quotes here
program test
    implicit none
    character (len=:), allocatable :: astring
    astring = STRINGIFY(MYMACRO)
    write(*,*), astring
end program test

Составлено с

gfortran -cpp -DMYMACRO=\"hello\" test.f90

Параметр -cpp включает препроцессор для всех расширений.

person sfinkens    schedule 14.09.2016
comment
Ну да, но дело в операторе # для конкатенации двух строк. Макрос, который вы показываете, буквально ничего не делает. - person Vladimir F; 15.09.2016