Макрос 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\"
но это вызвало не связанную проблему в процессе сборки.
Спасибо за помощь
3 ответа
Ваша попытка использовать хорошо известный рецепт строкового преобразования препроцессора C, а именно:
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
дает сбой по двум причинам, каждая из которых сама по себе достаточна.
Первый и самый простой, исходный файл, в котором вы пытаетесь использовать его, очевидно, имеет расширение .f90
, Что означает это расширение gfortran
(и к драйверу компилятора GCC под любым другим именем): Исходный код Fortran в свободной форме, который не должен подвергаться предварительной обработке. так же .f95
, .f03
а также .f08
, Если ты хочешь gfortran
сделать вывод, что исходный файл содержит код Фортрана свободной формы, который должен быть предварительно обработан, дать ему одно из расширений .F90
, .F95
, .F03
или же .F08
, Смотрите документацию GCC по этим вопросам.
Даже если вы делаете эту простую вещь, вторая причина кусается.
Использование препроцессора C для предварительной обработки исходного кода на Фортране так же старо, как и на С (который, хотя и старый, намного моложе, чем Фортран). gfortran
обязан не нарушать древний рабочий код; поэтому, когда он вызывает препроцессор C, он вызывает его в традиционном режиме. Традиционный режим препроцессора C - это способ, которым препроцессор вел себя до первой стандартизации языка C (1989), поскольку это нестандартное поведение может быть ограничено. В традиционном режиме препроцессор не распознает строковый оператор "#", который был введен первым стандартом Си. Вы можете убедиться в этом, вызвав препроцессор напрямую, например:
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
языковая опция. Диалекты Фортрана, которые вы можете указать таким образом, 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
соответствуют самым ранним стандартам С. Это так близко к -traditional
как мы можем получить, все еще пользуясь #
-оператор, от которого зависит рецепт строкового преобразования, но с ним вы получаете возможность взломать некоторый код на Фортране, если вы предварительно обработаете его таким образом; иначе gfortran
само по себе не будет обеспечивать -traditional
, В случае вашей тестовой программы вы можете смело опустить -std=c89
, позволяя cpp
чтобы соответствовать стандарту C по умолчанию, когда он был построен. Но если вы разрешите или направите его на -std=c99
или позже, тогда стандарт потребует признания //
как начало однострочного комментария (согласно C++), при котором любая строка Fortran, содержащая оператор конкатенации, будет обрезана при первом появлении.
Естественно, если вы используете, используете make
или другая система сборки для сборки кода, в котором вы хотите использовать строковые макросы, у вас будет возможность сообщить системе сборки, какие действия составляют компиляцию заданного класса компилируемых файлов. Для любого исходного файла Фортрана fsrc
Вы хотите скомпилировать с преамбулой строкового определения, действия для указания будут в духе:
cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' fsrc.f90 | gfortran -x f95 -c -o fsrc.o -
Хотя это старый и отвеченный вопрос, я хотел добиться строкового макроса в 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
Это выглядит действительно некрасиво, но это делает работу.
Удаление цитат вокруг 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
опция включает препроцессор для всех расширений.