Fortran查询并打印出函数或子程序名称

发布于 2024-11-29 07:41:29 字数 246 浏览 1 评论 0原文

在 Fortran 中是否可以查询我所在的函数或子例程的名称?即,我用什么来代替“???”让它在屏幕上打印“my_subroutine”?

subroutine my_subroutine()
   write(*,*) ???
end subroutine my_subroutine

我试图找到一种方法来实现自定义调试器/分析器,只使用文本编辑器的搜索和替换机制。以编程方式查询我在代码中的位置会很有帮助。

Is it possible in Fortran to query the name of the function or subroutine that I am in? I.e., what do I put in place of '???' to get it to print 'my_subroutine' on the screen?

subroutine my_subroutine()
   write(*,*) ???
end subroutine my_subroutine

I am trying to find a way to implement a custom debugger/profiler using nothing but a text editor's search and replace mechanism. Programmatically querying my position in the code would be helpful.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

剑心龙吟 2024-12-06 07:41:29

您可以使用预处理器打印出文件名和行号。您可能需要利用预定义的预处理器符号 __LINE____FILE__。下面是一个例子:

在头文件中定义了一个预处理器宏(以便可以在多个位置使用),将其命名为errormsg.h

#define ERRORMSG(msg) write(0,'("There was an error at ",I4," in file ",/,A,/,"Error message: ",A)') __LINE__,__FILE__,msg

然后您可以将此头文件包含在您的程序、库或模块文件,例如:

#include "errormsg.h"

program main 

  ERRORMSG("not really an error...")
  call foo()

end program


subroutine foo()

  ERRORMSG("not an error too!")

end subroutine

ERRORMSG("not true an error...") 对于 Fortran 代码来说似乎是奇怪的语法,但它被使用宏定义的 c 预处理器所取代。因此,编译后,它看起来像:

write(0,'("There was an error at ",I4," in file ",/,A,/,"Error message: ",A)') __LINE__,__FILE__,"not really an error"

对于我的 ERRORMSG 宏,我选择使用 0 文件单元打印到 stderr。显然,您可以自由地随心所欲地编写消息,只要它能生成语法正确的 FORTRAN 代码即可。

要对其进行编译,您需要将标志传递给编译器,并且不同编译器之间的标志略有不同。例如,这对我有用:

gfortran -cpp -o errorTest errorTest.f90

也就是说,对于 gfortran,-cpp 在编译之前调用 c 预处理器。上述程序的输出如下所示:

There was an error at    5 in file 
errorTest.f90
Error message: not really an error...
There was an error at   13 in file 
errorTest.f90
Error message: not an error too!

这可能会产生您正在寻找的效果,特别是如果您每个文件只编写一个子例程。

You can use the preprocessor to print out the file name and line number. You might want take advantage of the predefined preprocessor symbols __LINE__ and __FILE__. Here's an example:

A preprocessor macro is defined in header file (so that it can be used in multiple locations), call it errormsg.h:

#define ERRORMSG(msg) write(0,'("There was an error at ",I4," in file ",/,A,/,"Error message: ",A)') __LINE__,__FILE__,msg

Then you can include this header file in your program, library or module files, for example:

#include "errormsg.h"

program main 

  ERRORMSG("not really an error...")
  call foo()

end program


subroutine foo()

  ERRORMSG("not an error too!")

end subroutine

The ERRORMSG("not really an error...") seems like weird syntax for fortran code, but it get's replaced by the c-preprocessor using the macro definition. So when this is compiled, it looks like:

write(0,'("There was an error at ",I4," in file ",/,A,/,"Error message: ",A)') __LINE__,__FILE__,"not really an error"

For my ERRORMSG macro, I chose to use the 0 file unit to print to stderr. You obviously have the freedom to write the message how ever you like, as long as it results in syntactical correct FORTRAN code.

Getting this to compile requires you to pass flags to the compiler, and they differ slightly from compiler to compiler. This worked for me, for example:

gfortran -cpp -o errorTest errorTest.f90

That is, for gfortran, -cpp invokes the c-preprocessor before compiling. The output from the above program looks like this:

There was an error at    5 in file 
errorTest.f90
Error message: not really an error...
There was an error at   13 in file 
errorTest.f90
Error message: not an error too!

This might have the effect you are looking for, especially if you write only one subroutine per file.

养猫人 2024-12-06 07:41:29

不,你不能。您想要实现的目标称为 反射,它在 Fortran 中不可用(也不重要的是用 C 或 C++ 编写)。

No, you can't. What you want to achieve is called reflection and it is not available in Fortran (nor in C or C++ for what matters).

雨落□心尘 2024-12-06 07:41:29

我找到了一种简单的半自动化方法来摆脱这种情况:使用正则表达式在 SUBROUTINE 声明之后添加 __FUNCTION__ 的硬编码定义。从 makefile 中完成将注意每次编译都会刷新 __FUNCTION__ 宏。

假设我们有一个 F77 列表,看起来像这个

文件“my-file.F”,

  SUBROUTINE my_sub(var1, var2, var3)
  INCLUDE 'some-include.PRM'
  INTEGER var1
  INTEGER var2

  ! the rest of my code here
  WRITE(*,*)__FUNCTION__

  END SUBROUTINE

我想将其转换为

文件“my_file.FF”

  SUBROUTINE my_sub(var1, var2, var3)
#undef __FUNCTION__
#define __FUNCTION__ "my_sub"
  INCLUDE 'some-include.PRM'
  INTEGER var1
  INTEGER var2

  ! the rest of my code here
  WRITE(*,*)__FUNCTION__

END SUBROUTINE

请注意,修改后的代码现在位于另一个源文件中:< code>my-file.FF

为此,我将以下几行添加到“Makefile”中,

my-file.o: my-file.F
    perl -pne 's/^(\s+SUBROUTINE\s*)([^(]+)(\(.*\))/$1$2$3\n#undef __FUNCTION__\n#define __FUNCTION__ _S($2)/ixms' 
lt; > 
lt;.F; \
    $(FC) $(CPPFLAGS) $(FCFLAGS) -c 
lt;.F -o $@

假设 FC 被定义为 fortran 编译器可执行文件,则应在文件中的所有子例程上执行以下过程

  • : __FUNCTION__ 宏可能是之前定义的。
  • 在 SUBROUTINE 定义下方两行添加 __FUNCTION__ 指令,其中包含子例程的名称。
  • 以另一个名称保存该文件。
  • 将新源代码编译到所需的目标文件中。

在本例中,结果应为 my-file.o

您可能已经注意到我也使用了宏 _S()。这是一个“字符串化”宏。你只需要将它添加到你的 fortran 文件的顶部(我将它放在我到处都包含的 config.h 中),

GNU 和 intel 有不同的实现:

#ifdef __INTEL_COMPILER
#define _S(x) #x
#else
#define _S(x) "x"
#endif 

I found an easy semi-automated way out of this situation: use regex to add a hardcoded definition of __FUNCTION__ right after the SUBROUTINE declaration. Done from within the makefile will take care that every compilation refreshes the __FUNCTION__ macro.

Suppose we have a F77 listing that looks like this

file 'my-file.F'

  SUBROUTINE my_sub(var1, var2, var3)
  INCLUDE 'some-include.PRM'
  INTEGER var1
  INTEGER var2

  ! the rest of my code here
  WRITE(*,*)__FUNCTION__

  END SUBROUTINE

I want to convert it to

file 'my_file.F.F'

  SUBROUTINE my_sub(var1, var2, var3)
#undef __FUNCTION__
#define __FUNCTION__ "my_sub"
  INCLUDE 'some-include.PRM'
  INTEGER var1
  INTEGER var2

  ! the rest of my code here
  WRITE(*,*)__FUNCTION__

END SUBROUTINE

Note the amended code is now located in another source file: my-file.F.F

To do this I added the following lines to 'Makefile'

my-file.o: my-file.F
    perl -pne 's/^(\s+SUBROUTINE\s*)([^(]+)(\(.*\))/$1$2$3\n#undef __FUNCTION__\n#define __FUNCTION__ _S($2)/ixms' 
lt; > 
lt;.F; \
    $(FC) $(CPPFLAGS) $(FCFLAGS) -c 
lt;.F -o $@

Assuming FC is defined as the fortran compiler executable, this should perform the following procedure on all the subroutines in the file:

  • undefine a __FUNCTION__ macro that was possibly defined earlier.
  • Add a __FUNCTION__ directive two lines below the SUBROUTINE definition, containing the subroutine's name.
  • save the file under another name.
  • compile the new source into the required object file.

The result should be my-file.o in this case.

You may have noticed that I'm using the macro _S() as well. This is a 'stringify' macro. You just need to add it to the top of your fortran file (I place it inside a config.h that I include everywhere)

There is a different implementation for GNU and intel:

#ifdef __INTEL_COMPILER
#define _S(x) #x
#else
#define _S(x) "x"
#endif 
千笙结 2024-12-06 07:41:29

编译器有时会提供非标准功能来帮助您打印当前位置。这些是高度特定于编译器的,只能用于调试。

在 gfortran 中,您可以使用子例程 BACKTRACE。从手册中:

BACKTRACE 显示用户代码中任意位置的回溯。
之后程序继续正常执行。

输出看起来像一条错误消息,但它可能会有所帮助。

There are sometimes non-standard features in compilers to help you to print where you currently are. These are highly compiler specific and should be used only for debugging.

In gfortran you can use subroutine BACKTRACE. From the manual:

BACKTRACE shows a backtrace at an arbitrary place in user code.
Program execution continues normally afterwards.

The output will look like an error message, but it may be helpful.

云淡月浅 2024-12-06 07:41:29

为什么不在 WRITE 语句中硬写出您所在的子例程的名称呢?

您无法以编程方式(动态)给出或更改子例程的名称,因此我认为没有理由尝试以这种方式访问​​它(关于这一点:虽然我不确定是否不可能以某种方式访问​​它,我很确定这是错误的方法......你这样做会给自己带来更多麻烦,而不是仅仅对其进行硬编码)。

顺便说一句,你为什么要打印它呢?措辞良好的诊断消息不是能提供更多信息吗?

Why don't you just hard write the name of the subroutine you're in, in the WRITE statement?

You cannot programmatically (dynamically) give or change the name of the subroutine, therefore I see no reason to try to access it either that way (about that: while I'm not sure that it is impossible to access it somehow, I'm quite sure that it is the wrong way to go ... you will cause yourself more trouble going that way, than just hard coding it).

Btw, why are you trying to print it out anyway? Wouldn't a well phrased diagnostic message be more informative?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文