如何使用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\"
但是这在构建过程中引起了无关的问题。
感谢您的帮助
答案 0 :(得分:3)
您尝试使用C预处理器的众所周知的字符串化配方, 即:
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
失败有两个原因,每个原因都足够了。
首先也是最简单的,您尝试使用它的源文件
扩展名为.f90
。此扩展程序对gfortran
表示什么(以及
GCC编译器驱动程序,由任何其他名称)是:自由形式的Fortran源代码,不应该被预处理。
同样地.f95
,.f03
和.f08
。如果您希望gfortran
推断出来源
文件包含自由形式的Fortran代码必须进行预处理,给它一个
扩展程序.F90
,.F95
,.F03
或.F08
。见the GCC documentation of
these points
然而,即使你做了那么简单的事情,第二个原因就是叮咬。
使用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
输出是:
# 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
接近,同时仍然可以利用。#
gfortran
- 操作符,字符串化配方依赖于它,但是你拥抱它
如果以这种方式预处理它,可能会破坏某些 Fortran代码;
否则-traditional
本身不会强制执行-std=c89
。如果是
您的测试程序,您可以安全地省略cpp
,允许-std=c99
符合
在构建时使用默认的C标准。但是如果你允许或指导它
为了符合//
或更晚,标准将要求承认
make
作为单行注释的开头(根据C ++),任何一行
包含连接运算符的Fortran将被截断
第一次出现。
当然,如果您使用fsrc
或其他构建系统来构建
你想要字符串化宏的代码,你将有一种方法
构建系统构成编译给定类可编译的动作
文件。对于您想要编译的任何Fortran源文件cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' fsrc.f90 | gfortran -x f95 -c -o fsrc.o -
字符串化前导码,指定的动作将是静脉:
{{1}}
答案 1 :(得分:0)
删除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
选项为所有扩展启用预处理器。
答案 2 :(得分:0)
虽然这是一个陈旧且回答的问题,但我想在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
它看起来很丑陋,但它确实完成了工作。