Jim*_*iot 7 preprocessor fortran gfortran stringification stringify
如何使用 GNU gfortran 对预处理器宏进行字符串化?我想将宏定义传递给 GNU gfortran,然后将其用作代码中的字符串。
实际上,我想这样做:
program test
implicit none
character (len=:), allocatable :: astring
astring = MYMACRO
write (*, *) astring
end program test
Run Code Online (Sandbox Code Playgroud)
然后构建:
gfortran -DMYMACRO=hello test.F90
Run Code Online (Sandbox Code Playgroud)
我尝试创建各种宏,例如:
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
...
astring = STRINGIFY(MYMACRO)
Run Code Online (Sandbox Code Playgroud)
但这不适用于 gfortran 预处理器。
我还尝试使用不同风格的宏:
#define STRINGIFY(x) "x"
...
astring = STRINGIFY(MYMACRO)
Run Code Online (Sandbox Code Playgroud)
但这只会创建一个包含文本“MYMACRO”的字符串。
然后我尝试将宏定义更改为:
-DMYMACRO=\"hello\"
Run Code Online (Sandbox Code Playgroud)
但这在构建过程中导致了无关的问题。
感谢您的帮助
您尝试使用 C 预处理器的著名字符串化方法,即:
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
Run Code Online (Sandbox Code Playgroud)
失败有两个原因,每个原因本身就足够了。
首先,也是最简单的,您尝试在其中使用它的源文件显然具有扩展名.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
Run Code Online (Sandbox Code Playgroud)
wheretest.c包含一些尝试使用字符串化方法的尝试。尝试失败。
你不能哄gfortran自己去工作字符串化配方。
但是有一个解决方法。您可以cpp直接调用,不受传统模式的影响,预处理要在其中完成字符串化的 Fortran 源代码并将其输出中继到gfortran. 如果您已经知道这一点并且正在寻找一个gfortran单独的解决方案,则无需进一步阅读。
以这种方式在测试源中进行字符串化将如下所示:
cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' '-DMYMACRO=STRINGIFY(hello)' test.f90
Run Code Online (Sandbox Code Playgroud)
其输出是:
# 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
Run Code Online (Sandbox Code Playgroud)
该输出就是您要编译的内容。您也可以通过以下方式实现:
cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' test.f90 > /tmp/test.f90 \
&& gfortran -o test /tmp/test.f90
Run Code Online (Sandbox Code Playgroud)
然后你会发现它./test存在并且执行它输出hello。
您可以通过进一步细化消除中间临时文件。您的 F90 源代码将编译为 F95,因为后者是前者的保守。因此,如果您使用其-x 语言选项告诉它您正在使用哪种语言,那么您可以利用 GCC 将编译通过管道传输到其标准输入的源代码这一事实。在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 -
Run Code Online (Sandbox Code Playgroud)
与之前的解决方案一样有效,减去临时文件,并发出无害警告:
Warning: Reading file '<stdin>' as free form
Run Code Online (Sandbox Code Playgroud)
(注意并保留 - 命令行上gfortran的 final 。这就是编译标准输入的内容。)。的含义-x f95带来了额外的经济性,即由 预处理的源cpp不会被编译器再次预处理。
-std=c89调用时使用该选项cpp需要谨慎的解释。它具有使cpp符合最早的 C 标准的效果。这-traditional与我们在仍然使用#字符串化配方所依赖的-operator 的情况下所能获得的
最接近,但是如果您以这种方式对其进行预处理,那么您就有可能破坏某些Fortran 代码;否则gfortran本身不会强制执行-traditional。对于您的测试程序,您可以安全地省略-std=c89,从而cpp在构建时符合默认的 C 标准。但是如果你允许或指示它符合-std=c99或以后,那么标准将强制承认// 作为单行注释的开头(按照 C++),其中包含连接运算符的任何 Fortran 行将在第一次出现时被截断。
自然地,如果您使用正在使用make或其他构建系统来构建您想要字符串化宏的代码,您将有一种方法告诉构建系统哪些操作构成了编译给定类的可编译文件。对于fsrc您想要使用字符串化序言编译的任何 Fortran 源文件,要指定的操作如下:
cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' fsrc.f90 | gfortran -x f95 -c -o fsrc.o -
Run Code Online (Sandbox Code Playgroud)