在Fortran中是否可以查询我所在的函数或子例程的名称?即,我应该代替'???'让它在屏幕上打印'my_subroutine'?
subroutine my_subroutine()
write(*,*) ???
end subroutine my_subroutine
我试图找到一种方法来实现自定义调试器/分析器,只使用文本编辑器的搜索和替换机制。以编程方式查询我在代码中的位置会很有帮助。
答案 0 :(得分:10)
答案 1 :(得分:7)
您可以使用预处理器打印出文件名和行号。您可能希望利用预定义的预处理器符号__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 really an error...")
似乎是fortran代码的奇怪语法,但它被c-preprocessor替换为使用宏定义。因此,当编译它时,它看起来像:
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!
这可能会产生您正在寻找的效果,特别是如果您只为每个文件编写一个子例程。
答案 2 :(得分:3)
我发现了一种简单的半自动方式:在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.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
请注意,修改后的代码现在位于另一个源文件中:my-file.F.F
为此,我将以下行添加到'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' $< > $<.F; \
$(FC) $(CPPFLAGS) $(FCFLAGS) -c $<.F -o $@
假设FC被定义为fortran编译器可执行文件,这应该对文件中的所有子例程执行以下过程:
在这种情况下,结果应为my-file.o
。
您可能已经注意到我也在使用宏_S()。这是一个'stringify'宏。你只需要将它添加到你的fortran文件的顶部(我把它放在我包含在任何地方的config.h中)
GNU和intel有不同的实现:
#ifdef __INTEL_COMPILER
#define _S(x) #x
#else
#define _S(x) "x"
#endif
答案 3 :(得分:2)
为什么不在WRITE语句中硬写下你所在的子程序的名称?
您无法以编程方式(动态)提供或更改子例程的名称,因此我认为没有理由尝试以这种方式访问它(关于那个:虽然我不确定它是不可能以某种方式访问它,我很确定这是错误的方式...你会让自己更麻烦这样做,而不仅仅是硬编码)。
顺便问一下,你为什么要把它打印出去呢?准确的诊断信息不会提供更多信息吗?答案 4 :(得分:1)
编译器中有时会出现非标准功能,可帮助您打印当前所在的位置。这些是高度编译器特定的,应该只用于调试。
在gfortran中,您可以使用子程序BACKTRACE。从手册:
BACKTRACE在用户代码中的任意位置显示回溯。 之后程序继续执行。
输出看起来像是一条错误消息,但它可能会有所帮助。