Стрим-макрос с 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\"

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

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

Ответ 1

Ваша попытка использовать известный рецепт строчки препроцессора С, а именно:

#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 (который, хотя старый, намного моложе Фортрана). gfortran обязан не разрушить древний рабочий код; поэтому, когда он вызывает препроцессор С, он вызывает его в традиционном режиме. Традиционный режим препроцессора C это способ, которым препроцессор вел себя до первой стандартизации языка C (1989), поскольку это нестандартное поведение может быть закреплено. В традиционных режиме, препроцессор не распознает оператор стробирования '#', который был введенный первым стандартом C. Вы можете проверить это, вызвав препроцессор прямо как:

cpp -traditional test.c

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

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

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

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

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. Набранные диалекты Fortran, которые вы можете указать таким образом, это 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, как мы можем получить, все еще пользуясь # -оператор, от которого зависит рецепт строения, но с этим вы обнимаете возможность взломать некоторый код Fortran, если вы его предварительно обработаете; иначе gfortran сам не обеспечивал бы -traditional. В случае ваша тестовая программа, вы можете спокойно опустить -std=c89, позволяя cpp соответствовать к стандарту C по умолчанию, когда он был построен. Но если вы разрешаете или направляете это для соответствия -std=c99 или более позднему, тогда стандарт будет выдавать мандат из // в качестве начала однострочного комментария (согласно С++), с помощью которого любая строка Fortran, который содержит оператор конкатенации, будет усечен на первое вхождение.

Естественно, если вы используете 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 -

Ответ 2

Удаление цитат вокруг 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 включает препроцессор для всех расширений.

Ответ 3

Хотя это старый и ответивший вопрос, я хотел добиться макро-строкования в 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

Это выглядит очень уродливо, но он выполняет свою работу.