使用 GNU gfortran 将宏字符串化
Stringify macro with 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\"
但这在构建过程中引起了不相关的问题。
感谢您的帮助
您尝试使用众所周知的 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
-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
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 标准。
在仍然利用
#
-operator,字符串化配方所依赖的,但是你拥抱它
如果以这种方式进行预处理,可能会破坏 一些 Fortran 代码;
否则 gfortran
本身不会强制执行 -traditional
。如果是
你的测试程序,你可以安全地省略 -std=c89
,允许 cpp
符合
到构建时的默认 C 标准。但如果你允许或指示它
符合 -std=c99
或更高版本,则标准将强制认可
//
作为单行注释的开头(根据 C++),其中任何一行
包含串联运算符的 Fortran 将在
第一次出现。
当然,如果您使用 make
或其他构建系统来构建
您想要字符串化宏的代码,您将有一种方法可以告诉您
构建系统什么动作构成 compiling 给定的 class compilable
文件。对于您想要编译的任何 Fortran 源文件 fsrc
字符串化序言,要指定的操作将是静脉:
cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' fsrc.f90 | gfortran -x f95 -c -o fsrc.o -
删除 STRINGIFY
中 x
周围的引号解决了我的问题:
#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
选项为所有扩展启用预处理器。
尽管这是一个已回答的老问题,但我想在不更改默认预处理器或构建过程的情况下在 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
它看起来真的很丑,但它确实完成了工作。
如何使用 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\"
但这在构建过程中引起了不相关的问题。
感谢您的帮助
您尝试使用众所周知的 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
-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
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 标准。
在仍然利用
#
-operator,字符串化配方所依赖的,但是你拥抱它
如果以这种方式进行预处理,可能会破坏 一些 Fortran 代码;
否则 gfortran
本身不会强制执行 -traditional
。如果是
你的测试程序,你可以安全地省略 -std=c89
,允许 cpp
符合
到构建时的默认 C 标准。但如果你允许或指示它
符合 -std=c99
或更高版本,则标准将强制认可
//
作为单行注释的开头(根据 C++),其中任何一行
包含串联运算符的 Fortran 将在
第一次出现。
当然,如果您使用 make
或其他构建系统来构建
您想要字符串化宏的代码,您将有一种方法可以告诉您
构建系统什么动作构成 compiling 给定的 class compilable
文件。对于您想要编译的任何 Fortran 源文件 fsrc
字符串化序言,要指定的操作将是静脉:
cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' fsrc.f90 | gfortran -x f95 -c -o fsrc.o -
删除 STRINGIFY
中 x
周围的引号解决了我的问题:
#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
选项为所有扩展启用预处理器。
尽管这是一个已回答的老问题,但我想在不更改默认预处理器或构建过程的情况下在 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
它看起来真的很丑,但它确实完成了工作。