Arrays and data manipulation
10.3 Module procedures
The procedures we have so far met have been “external” procedures, i.e. subroutines and functions that are external to the main program. External procedures are extremely useful and in practice they form the bulk of almost all large Fortran programs. They have the incidental advantage that they need not necessarily be supplied in the Fortran language: in principle it is possible for a Fortran program to call external procedures that are in another language or in precompiled machine code. Almost everything that can be achieved with Fortran can be done using a set of external procedures, a main program, and data modules.
Nevertheless, Fortran allows for another kind of procedure, “module procedures”, which are specified within modules. Like external procedures, module procedures are either of the “subroutine” or the
“function” variety; they look very similar to external procedures and are invoked in exactly the same way by CALL statements or function references. However, a module procedure may only be invoked from parts of the program to which the module is plugged in by USE statements (or from another module procedure in the same module). Module procedures must always be supplied in Fortran.
There are a number of reasons why module procedures can be useful. If a module defines the structure of a special set of data, and if there are also some specialized procedures needed for operating on that data, then it can be appropriate to include the procedures as well as the data in the one module. Even if no special data set is involved, it can be handy to use a module to hold a “library” of related procedures. A programmer may create a set of procedures with a frequently encountered problem in mind, e.g. reading disk files in various formats, and putting them into a module that could be copied and used in a number of different programs. Another point is that procedures with the same name can sit in different modules, and then the programmer can switch between different versions of the same procedure just by changing a USE statement.
A special statement, CONTAINS, is required when a module contains module procedures. The general form of a module is
MODULE Name USE statements declarative statements CONTAINS
module procedures END MODULE Name
When this module is invoked by a USE statement in any other subprogram, that subprogram has access to the data sets declared in the module and may call any of the procedures that follow the CONTAINS statement. It also has access to the additional modules, if any, identified by USE statements at the beginning of this module. The declarative statements may be a mixture of type declaration statements and the other kinds of declaration mentioned at the end of the last section, but may not include executable statements.
After CONTAINS, there is a set of one or more module procedures. Module procedures are exactly the same in form as external subroutines or functions, each starting with a SUBROUTINE (or FUNCTION) statement and finishing with a suitable END statement. All the sorts of subroutines and functions explained in Chapters 7 and 11 may occur as module procedures rather than as external procedures.
There is one very important difference between module procedures and external procedures, namely that a module procedure has direct access to the data declared in the module before the CONTAINS statement.
This is called the “host association” of data, and the module is said to be the “host” of its module procedures. Entities declared before the CONTAINS statement are shared by host association among all the module procedures following.
Here is an example of a module with data and procedures:
MODULE Polynomial REAL :: c(0:3) CONTAINS
SUBROUTINE Show_Coefficients WRITE (*,*) C
END SUBROUTINE Show_Coefficients FUNCTION Poly(x)
REAL :: Poly
REAL, INTENT(IN) :: x
Poly = c(0) + x*(c(1) + x*(c(2) + x*c(3))) END FUNCTION Poly
FUNCTION Inverse_Poly(y) REAL :: Inverse_Poly REAL, INTENT(IN) :: y REAL :: scratchpad(4) .
. .
(finds an approximate real solution to the cubic equation y=poly(x),
. . .
with Poly as defined as above)
Inverse_Poly =…
END FUNCTION Inverse_Poly SUBROUTINE Switchback c(0:3) = c(3:0:–1)
END SUBROUTINE Switchback END MODULE Polynomial
The subroutines Show_Coef f icients and Switchback, and the functions Poly and inverse_Poly, can be accessed by any other program unit that has the statement
USE Polynomial
These module procedures may also access one another: for example the function inverse_poly will include calls to Poly. Moreover, the data declared at the beginning of the module above (the array c) is also directly accessible to all the module procedures and to other parts of the program using the module. To avoid a clash of names, however, the programmer should obviously avoid the use of the name c for any other, different, piece of data in any module procedure.
At the same time, a module procedure may have data items internal to itself and not accessible outside it.
This is the situation if a module procedure has some type declaration statements of its own: the items declared will be “local” variables and will not be accessible by host association outside the module procedure. For example, the function inverse_Poly above includes a data array called scratchpad, and this is a local variable not accessible anywhere else. The argument y in inverse_Poly, and also the argument x in Poly, are names local to those procedures and not accessible to the rest of the module, but they are also dummy arguments and are therefore accessible (by “argument association”, not “host association”!) from wherever the functions are called.
Note that if one of a module’s own data declarations is repeated within a module procedure, the effect is to set up a separate local variable and therefore to prevent the module procedure from accessing the module’s data. Consequently,
MODULE Pointless REAL :: x
CONTAINS
SUBROUTINE Double REAL :: x
x = 2.0 * x
END SUBROUTINE Double END MODULE Pointless
will achieve nothing because, within the subroutine, x is a local variable and is not the same piece of data as the x declared before the CONTAINS statement! But, if the subroutine’s REAL statement were removed, then its x would be host-associated to the original x, and a statement CALL Double would double the value of x.
10.4
Data association by argument, host and USE
“Local” variables have no significance whatsoever outside the program unit in which they are declared: they have the “scope” of a single program unit. However, data may be shared between program units by
“association”. There are three distinct forms of association:
• Arguments of procedures allow data to be passed between program units as the program is executing.
Just as a door has two sides, an argument exists both as a “dummy” argument in the procedure itself and as an “actual” argument in a calling program. When a procedure is invoked, there is “argument association” between the actual arguments and the dummy arguments. In the case of a function, a similar thing happens with its value. Although “argument association” passes a value between one program unit and another, the names of arguments should be declared separately in the two program units.
• When variables are declared at the beginning of a module that also contains module procedures, then the variables are also accessible within the module procedures by “host association”.
• When a USE statement connects a module to a subprogram, the variables declared at the start of the module (i.e. before any CONTAINS statement) are said to be “USE associated” to the subprogram.
The three kinds of association (argument, host and USE association) should be kept quite distinct from one another. A crucial point is that argument association happens dynamically as the program executes, with values being passed from one memory location to another. By contrast, host association and use association are static relationships that are established once and for all when the program is compiled. Host association and use association do not involve any copying or duplication of data: they are merely forms of shared access to particular memory locations.
USE association normally means that all the variables declared in a module (except those local to module subprograms) are accessible within any subprogram which USES the module. However, it can sometimes be helpful to limit this association to a subset of the module’s variables. This can be achieved with the help of two attributes that can be applied to type declaration statements in a module, namely PRIVATE and PUBLIC. Data having the PRIVATE attribute cannot be USE-associated, while data having the PUBLIC attribute can. The default situation is PUBLIC, but this can be reversed with the single statement
PRIVATE
which, occurring at the beginning of a module, will make all the data non-USE-accessible unless it is countermanded by specifying PUBLIC in the TDS referring to a particular piece of data. It is also possible to have PRIVATE and PUBLIC statements listing data items that have already been declared in TDSS.
The PUBLIC and PRIVATE attributes can be applied not only to variables but also to other objects such as named constants and the names of module procedures. For example, suppose we have a complicated module called Charsets that, among many other things, defines an operator called . intersect, that identifies the characters that are common to two character strings, i.e.
string1.intersect.string2
is a string consisting of the characters that are to be found both in string1 and in string2. The module Char set must contain a function linked to the .intersect. operator. However, if Charset starts with
MODULE Charset PRIVATE
PUBLIC :: OPERATOR (.intersect.) .
. .
then it makes the operator usable in any program unit that USES Charset, while keeping hidden all the other entities in Charset. This will be made a little clearer by Section 10.6. More generally, to have
MODULE Name PRIVATE
PUBLIC :: list of entities to be accessible from outside .
. .
is a more positive and safer way of allowing USE association than doing it the other way around with MODULE Name
PRIVATE :: list of entities not to be accessible from &
outside . . .
10.5