使用GNU gfortran对宏进行Stringify

时间:2015-07-27 09:38:16

标签: fortran preprocessor gfortran stringify stringification

如何使用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 个答案:

答案 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方言是f77f77-cpp-inputf95f95-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)

删除xSTRINGIFY周围的引号为我解决了问题:

#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

它看起来很丑陋,但它确实完成了工作。