Scope of variables in case of modules used by modules in Fortran

久未见 提交于 2020-01-11 10:03:46

问题


I've noticed that variables in a (child) module, which are used by a parent modules are accessible in a main program through just the parent module. This is one concept which clearly distinguishes use statement in Fortran from include statement in C/C++. The following programs clearly illustrate this concept.

a.f90

module a_mod
 use b_mod
 implicit none

 integer :: a
end module

b.f90

module b_mod
  use c_mod
  implicit none

  integer :: b

end module

c.f90

module c_mod
  implicit none

  integer :: c = 10

contains

  subroutine inc_c
    c = c + 10
  end subroutine inc_c
end module

test.f90

program test
  use a_mod
  implicit none

  call inc_c
  write(*,*),c
end program

Note that I am able to call a function in c_mod by just using a_mod. Note that I cannot directly observe that c_mod is available unless I traverse the dependency list.

But in a complicated software, is there a simple way to know (say, using an IDE) if a variable is available for use at a particular line?


回答1:


The best thing to do, in my opinion, is to avoid the use of blanket use statements, especially for large and sometimes unwieldy modules. Instead, specify which module entities to inherit via the only keyword, such as:

program main
   use a_mod, only : c, inc_c
   implicit none

   call inc_c
   write(*,*) c
end program main

This works, but it's confusing because a_mod isn't the real owner of c and inc_c. Therefore, you should try to use entities from where they are actually declared, which gives:

program main
   use c_mod, only : c, inc_c
   !   ^ This has changed
   implicit none

   call inc_c
   write(*,*) c
end program main

Now, anybody reading the code has a clear notion of which variables and subroutines are in scope and where they come from.

Finally, this has the added benefit of reducing the risk that you use c without realizing it's actually inhereted from c_mod. This is particularly a problem when not using implicit none!




回答2:


As Vladimir F suggested in a comment, you can solve this issue using private and public statements in your modules. If you rewrite your modules like this:

module a_mod
  use :: b_mod

  private
  public :: a

  integer :: a
end module

module b_mod
  use :: c_mod

  private
  public :: b

  integer :: b
end module

module c_mod
  private
  public :: c, inc_c

  integer :: c = 10
contains
  subroutine inc_c
    c = c + 10
  end subroutine
end module

In this case, the statement private in the beginning of each module means that quantities declared in the module are not exported by default. You now have to explicitly declare what variables and subroutines to make available when you use the module by adding a public statement. (This could alternatively be done in one line using the syntax integer, public :: c = 10.) This practice prevents c_mod variables from leaking out of b_mod, and so on.




回答3:


Though not inituitve like IDE, gfortran can print a list of imported symbols by attaching the -fdump-fortran-original (or -fdump-parse-tree) option. To do so, we first generate *.mod file as

gfortran -c {c,b,a}.f90

and compile the desired source as

gfortran -fdump-fortran-original -c test.f90

Then, we get a list of imported symbols like this:

Namespace: A-Z: (UNKNOWN 0)
procedure name = test
  symtree: 'a'           || symbol: 'a'            
    type spec : (INTEGER 4)
    attributes: (VARIABLE IMPLICIT-SAVE USE-ASSOC(a_mod))
  symtree: 'a_mod'       || symbol: 'a_mod'        
    type spec : (UNKNOWN 0)
    attributes: (MODULE  USE-ASSOC(a_mod))
  symtree: 'b'           || symbol: 'b'            
    type spec : (INTEGER 4)
    attributes: (VARIABLE IMPLICIT-SAVE USE-ASSOC(b_mod))
  symtree: 'b_mod'       || symbol: 'b_mod'        
    type spec : (UNKNOWN 0)
    attributes: (MODULE  USE-ASSOC(b_mod))
  symtree: 'c'           || symbol: 'c'            
    type spec : (INTEGER 4)
    attributes: (VARIABLE IMPLICIT-SAVE USE-ASSOC(c_mod))
  symtree: 'c_mod'       || symbol: 'c_mod'        
    type spec : (UNKNOWN 0)
    attributes: (MODULE  USE-ASSOC(c_mod))
  symtree: 'inc_c'       || symbol: 'inc_c'                             <---        
    type spec : (UNKNOWN 0)                                             <---
    attributes: (PROCEDURE MODULE-PROC  USE-ASSOC(c_mod) SUBROUTINE)    <---
  symtree: 'test'        || symbol: 'test'         
    type spec : (UNKNOWN 0)
    attributes: (PROGRAM PUBLIC  SUBROUTINE)

(For example, the lines with arrow indicates that a routine inc_c is available from c_mod.) If we modify test.f90 so as to attach the only keyword,

program test
  use a_mod, only: inc_c
  implicit none

  call inc_c
end program

then the output is also simplified accordingly:

Namespace: A-Z: (UNKNOWN 0)
procedure name = test
  symtree: 'a_mod'       || symbol: 'a_mod'        
    type spec : (UNKNOWN 0)
    attributes: (MODULE  USE-ASSOC(a_mod))
  symtree: 'inc_c'       || symbol: 'inc_c'        
    type spec : (UNKNOWN 0)
    attributes: (PROCEDURE MODULE-PROC  USE-ASSOC(c_mod) SUBROUTINE)
  symtree: 'test'        || symbol: 'test'         
    type spec : (UNKNOWN 0)
    attributes: (PROGRAM PUBLIC  SUBROUTINE)

So, althogh I've never used this option for this purpose, it might be of some use for the OP's purpose (if really necessary).



来源:https://stackoverflow.com/questions/42474530/scope-of-variables-in-case-of-modules-used-by-modules-in-fortran

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!