Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fortran query and print out function or subroutine name

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.

like image 558
drlemon Avatar asked Aug 11 '11 21:08

drlemon


3 Answers

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).

like image 137
Stefano Borini Avatar answered Sep 20 '22 20:09

Stefano Borini


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.

like image 37
Yann Avatar answered Sep 21 '22 20:09

Yann


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' $< > $<.F; \
    $(FC) $(CPPFLAGS) $(FCFLAGS) -c $<.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 
like image 43
superk Avatar answered Sep 18 '22 20:09

superk