Overloading and Generic Programming

Overloading is when a procedure or operator is able to work on multiple types. Most languages overload at least basic arithmetic operators to work on all numerical types. In modern Fortran the mathematical intrinsics are overloaded to work at least on real and double precision, and when appropriate complex. For example, in older Fortran there were three versions of every trigonometric function when defined for complex numbers. Cosine was COS for real number, DCOS for doubles, and CCOS for complex. In modern Fortran COS works for all three. This is an example of generic programming.

Overloading can be more general than just numerical types. In some languages, sort is an intrinsic and works on any types for which relational operators (<,<=,>,>=) are defined.

Programmers can overload their own procedures in Fortran. To make a generic procedure, an interface is required. In this case the interface block has a name, which will be the generic name, and only the specific procedures should be included.

In all the examples below, our new generic diag for computing the diagonal of a matrix, will accept either real or double precision arrays. The extension to complex should be obvious if it is needed.

Explicit Interface Block

We can use an explicit interface block in a non-module program unit.

interface diag
    function rdiag(A)
       use precisions
       implicit none
       real (sp), dimension(:,:), intent(in) :: A
       real (sp), dimension(size(A,2))       :: rdiag
    end function
    function ddiag(A)
       use precisions
       real (dp), dimension(:,:), intent(in) :: A
       real (dp), dimension(size(A,2))       :: ddiag
    end function
end interface

Module Procedure

Overloaded procedures are usually and most appropriately defined in a module. Since procedures in modules already have an implicit interface, we cannot use the explicit interface above. We still use a named interface block with MODULE PROCEDURE

interface diag
    module procedure rdiag
    module procedure ddiag
end interface

The body of the functions would be in the module.

Generic in Type-Bound Procedures

If we wanted to define something with various linear-algebra functions defined on it, we would use the GENERIC keyword. Note that the specific functions must be written so that their signatures, the number and types of their arguments, are differ, or the compiler will not be able to distinguish them.

type something
   !variables
   contains
      private
      procedure :: rdiag
      procedure :: ddiag
      generic,public :: diag=>rdiag,ddiag
end type

Operator Overloading

The arithmetic operators +,-,*,and / can be overloaded to work on programmer-defined derived types. Assignment (=) can also be overloaded when copying can be defined for the type.

Module Procedures

The arithmetic operators are overloaded with a generic interface, but rather than the name of the generic function, the keyword OPERATOR is used, followed by the symbol in parentheses for operators, or ASSIGNMENT(=) for copying.

interface operator(+)
   module procedure adder
end interface
interface operator(-)
   module procedure subber
end interface
interface assignment(=)
   module procedure assigner
end interface

The procedures that define the operator must be functions, must have two arguments, and must declare those arguments INTENT(IN). For example, suppose we wished to define adder for an Atom type to add the atomic masses:

type(Atom) function adder(atom_A,atom_B)
    type(Atom), intent(in) :: atom_A, atom_B
    adder%atomic_mass=atom_A%atomic_mass+atom_B%atomic_mass
end function

For assignment, the procedure must be a subroutine with two arguments. The first argument must represent the left-hand side and be INTENT(OUT), while the second represents the right-hand side and is INTENT(IN).

subroutine assigner(new_atom,old_atom)
   type(Atom), intent(out) :: new_atom
   type(Atom), intent(in)  :: old_atom
   new_atom%symbol=old_atom%symbol
   new_atom%eng_name=old_atom%eng_name
   new_atom%atomic_number=old_atom%atomic_number
   new_atom%atomic_mass=old_atom%atomic_mass
   new_atom%electronegativity=old_atom%electronegativity
end subroutine

Type-Bound Operators

Operators may be overloaded to work on class members. The syntax is somewhat different than for separate types in modules. Here is a snippet from a module defining a Fraction class. The rules for the arguments are the same as for modules, but of course the instance variable must be declared CLASS.

private

   public :: Fraction

   type Fraction
      private
      integer              :: num, denom
      contains
         private
         procedure adder
         procedure subber
         procedure multer
         procedure divver
         procedure copier
         procedure, public :: reduce
         procedure, public :: print=>printer
         generic, public   :: operator(+) => adder
         generic, public   :: operator(-) => subber
         generic, public   :: operator(*) => multer
         generic, public   :: operator(/) => divver
         generic, public   :: assignment(=) => copier
   end type
Previous
Next