Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
384 views
in Technique[技术] by (71.8m points)

variables - Host Association vs Use Association in Fortran

Are there any "general rules" as to when one is preferable to the other?

The context of this question is: I asked a different question regarding host association yesterday (link) and in the comments, I was advised to use host association with caution. The reason being that through host association, it is easy to inadvertently modify variables since the subroutines have unrestricted access to all variables that are declared in the module.

To illustrate this, I will use the following code example:

module mod
implicit none

real :: x

contains

subroutine sub(y)
  use other_mod, only: a

  real, intent(out) :: y

  y = a + x
  a = a + 1.
  x = x + 1.
end subroutine sub

end module mod

Both aand x are modified in sub. But for x, I need to go through all the code to see this. That a is used in sub (and possibly modified) can be seen easily by looking at the declaration part of sub.

In this sense, it seems preferable to have two kinds of modules:

  1. A module or modules only containing variable declarations (which are then used when needed)
  2. Modules that only contain procedures and possibly parameter declarations but no variable declarations

This gets rid of host association for variables altogether.

But this doesn't seem practical for a number of reasons:

  • I might have a dozen subroutines using (and modifying) the same variables in one module. Having to use these variables everytime clutters the code, especially if there are a lot of them (say a few hundred).
  • Seperating the declaration of a variable from where it is actually used seems to make the code less comprehensible:
    • Either, one creates one giant control file containing all the declarations. This could be quite confusing if the code is large and uses many variables.
    • Or, one creates a seperate control file for every module (or group of modules, if they depend on the same content). This would make the code itself better comprehensible, since using the variables immediately shows where they are coming from. But it would complicate the structure of the code, creating a vastly more complicated file structure (and accompanying dependency structure).

In the end, all of this boils down to: When is it more sensible to put the declaration of variables in the same module in which they are used (so that they are used by host association) and when is it more sensible to outsource the declaration to a seperate module (so that the variables will be used via use association when they are needed)?

Are there any general guidelines or should this be decided on a case by case basis? And if it is case by case, what are the reasons to go for one over the other?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Fortran provides several ways to create, store, use, and pass data between different "program units": the main program, external procedures, and modules.1 As you know, each program unit can contain internal procedures - which, through host association, have access to any variable or procedure contained within the host. This is often seen as an advantage. As mentioned already by @HighPerformanceMark in his comment, the general guideline for when to use host-association or use-association is:

use host-association when variables are only (or mainly) used by routines declared in the same module, and use use-association when you want to define variables to be used in many modules

From your comments, it sounds like most or all of the host variables in your main program are accessed by each internal procedure (about a dozen or so subroutines). If that's the case, then host-association seems like a very reasonable option, and there's really no need to pass in arguments to each subroutine explicitly. On the other hand, if each subroutine actually uses only a subset of the variables, then it might be reasonable to get more explicit about it.

Like you, I am generally uncomfortable with using variables within a procedure that haven't been declared in an argument list. This is partly because I like how the list of args is self-documenting, and it helps me to reason about the code and how data is manipulated within it. This is even more true when collaborating with other workers, or if I've spent some time away from the code and my memory of it has faded. However, I've discovered there is little reason to avoid host association altogether, as long as you are aware of how it works and have a strategy.

In fact, I tend to use internal procedures and host-association quite often, especially for short functions/subroutines. I find it helpful to loosely think of the host as the "object", its variables as "attributes", and any internal procedures very much like the object's "methods" that do the work. Of course, that's simplifying things, but that's really the point.

For more complex programs I reduce the amount of host-association from the "main" program itself, which then exists primarily to call the various subroutines in the proper order and context. In this case, we can take advantage of use-association and choose to use module entities (such as procedures, variables, types, parameters) directly within the program unit that needs them. We can further restrict access to only those module entities that are needed with only:. This aids readability, the data flow is clearly indicated, and I find that updating the code later is more straightforward. You know, inheritance, encapsulation, and whatnot...but Fortran style. Which is actually pretty good.

Here's an example program structure that works for me and the moderately-sized projects I've worked on in Fortran. I like to keep my widely-used (static) parameters in a separate module (or modules, if grouped according to function). I keep derived types and type-bound procedures in another separate module(s). If it's useful, I make certain module entities private, so that they are not accessible from other program units. And I guess that's it.

module params
    implicit none
    public              !! All items public/accessible by default.
    integer, parameter  :: dp = kind(0.d0)
    integer, parameter  :: nrows = 3
    real(dp), parameter :: one=1.0_dp, two=2.0_dp
    ...
end module params

module types
    use params, only: dp, nrows
    implicit none
    public              !! Public by default.
    private :: dim2
    ...
    integer, parameter :: dim2 = 3
    ...
    type :: A
        integer :: id
        real(dp), dimension(nrows,dim2) :: data
        contains
        procedure, pass :: init
    end type A
    ...
    contains
    subroutine init(self, ...)
        ...
    end subroutine init
    ...
end module types

module utils
    implicit none
    private             !! Private by default.
    public :: workSub1, workSub2, subErr
    ...
    integer,save :: count=0 !! Accessible only to entities in this module.
    ...
    contains
    subroutine workSub1(...)
        ...
    end subroutine workSub1

    subroutine workSub2(...)
        ...
    end subroutine workSub2

    subroutine subErr(...)
        ...
    end subroutine subErr

end module utils


program main
    !! An example program structure.
    use params, only: dp
    implicit none
    real(dp) :: xvar, yvar, zvar
    integer  :: n, i
    logical  :: rc

    call execute_work_subroutines()

    contains                !! Internal procs inherit all vars declared or USEd.
    subroutine execute_work_subroutines()
        use types, only: A
        type(A) :: DataSet
        !! begin
        call DataSet%init(i)
        do i = 1,n
            call workSub1(xvar,yvar,zvar,A,i,rc)
            if (rc) call subErr(rc)
            call workSub2(A,rc)
            if (rc) call subErr(rc)
        enddo
    end subroutine execute_work_subroutines
end program main

1There are also submodules, but I am not familiar with them and don't want to give misleading info. They do seem useful for logically separating large modules.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...