您尝试使用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
,不受传统模式的限制,预处理你希望进行字符串化的Fortran源代码,并将其输出传递给gfortran
。如果你已经知道了这个方法,并且正在寻找一个仅使用gfortran
的解决方案,那么就不需要继续阅读了。
使用此方式在测试源中进行字符串化的示例如下:
cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' '-DMYMACRO=STRINGIFY(hello)' 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选项。你可以指定的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
预处理,不会再次被编译器预处理。
在调用cpp
时使用选项-std=c89
需要谨慎说明。它的作用是使cpp
符合最早的C标准。这是我们在仍然使用字符串化配方依赖于#
运算符的情况下所能接受的最接近-traditional
的方式。但是如果您以这种方式对Fortran代码进行预处理,则可能会破坏一些Fortran代码;否则gfortran
本身就不会强制执行-traditional
。对于您的测试程序,您可以安全地省略-std=c89
,让cpp
符合构建时的默认C标准。但是,如果允许或指示其符合-std=c99
或更高版本,则标准将强制识别//
作为一行注释的开头(如C ++),从而使包含连接运算符的任何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 -
#define STRINGIFY(x) "x"
。我发现它只是创建了一个包含文本“MYMACRO”的字符串。 - Jim Eliot