BCS Logo
BRITISH COMPUTER SOCIETY Fortran Specialist Group

Fortran 2000
Programme
The Group
Comment
Overview
Exceptions and C
Derived types
OO features
Report

About Us
FSG Home page
Joining the Group
Contact details

Important Disclaimer

Data Manipulation Enhancements

Malcolm Cohen
The Numerical Algorithms Group Ltd.

Please note: this HTML file has been constructed directly from the slides used in the presentation.


Fortran 2000: DME Overview


ISO/IEC TR 15581 - just briefly

  • allocatable components
  • allocatable dummy arguments
  • allocatable function results

Rationale:

  • consistency
  • performance: contiguity
  • performance: no aliasing
  • convenience: no leaking

Allocatable components

  TYPE t
    REAL,ALLOCATABLE :: c(:,:)
  END TYPE

  SUBROUTINE s
    TYPE(t) x
    TYPE(t),SAVE :: y
    ...
  END SUBROUTINE

  • like variables, initially unallocated

    (So x%c is unallocated whenever subroutine s begins execution, and y%c is unallocated at the beginning of the program.)

  • like variables, automatically deallocated (unless SAVEd)

    (So x%c is deallocated on return from subroutine s, while y%c retains its allocation status.)

Allocatable components: assignment

  • unlike variables, sensible assignment
    ("deep copy'')

(So the assignment statement

    x = y
acts like
    IF (ALLOCATED(x%c)) DEALLOCATE(x%c)
    IF (ALLOCATED(y%c)) THEN
      ALLOCATE(x%c(lbound(y%c,1):ubound(y%c,1), &
                   lbound(y%c,2):ubound(y%c,2)))
      x%c = y%c
    END IF
And this is recursively applied for nested allocatable components.)

Rationale: Otherwise the bookkeeping would be prohibitive.

Allocatable components: example

MODULE matrix_module
  TYPE real_matrix
    REAL,ALLOCATABLE :: value(:,:)
  END TYPE
  INTERFACE OPERATOR(*)
    MODULE PROCEDURE multiply_mm
  END INTERFACE
  ...
CONTAINS
  TYPE(real_matrix) FUNCTION multiply_mm(a,b) RESULT(c)
    TYPE(real_matrix),INTENT(IN) :: a,b
    ALLOCATE(c%value(size(a%value,1),size(b%value,2)))
    c%value = matmul(a%value,b%value)
  END FUNCTION
END

PROGRAM example
  USE matrix_module
  TYPE(real_matrix) :: x,y,z
  ...
  x = y*z
  ...
END

Better than the pointer component version because it

  • is more efficient
  • does not leak memory
  • is easier to write (e.g. assignment already does the "right thing").

Allocatable dummy arguments

  SUBROUTINE load(arr,unit)
    REAL,ALLOCATABLE,INTENT(OUT) :: arr(:,:,:)
    INTEGER,INTENT(IN) :: unit
    INTEGER n1,n2,n3
    READ(unit) n1,n2,n3
    ALLOCATE(arr(n1,n2,n3))
    READ(unit) arr
  END

Notes:

  • INTENT(OUT) means it starts out unallocated.

Allocatable function results

  FUNCTION read_array(unit) RESULT(array)
    REAL,ALLOCATABLE :: array(:,:,:)
    INTEGER n1,n2,n3
    READ(unit) n1,n2,n3
    ALLOCATE(array(n1,n2,n3))
    READ(unit) array
  END

Notes:

  • The result is initially unallocated.
  • The result is automatically deallocated after its value has been used in the calling expression.
  • Not hugely useful (specification functions have largely obviated the need for this functionality) though some things - like the example above - cannot be written using specification functions (in this case, because specification functions must be PURE and PURE functions cannot do external i/o).

Back to the top

Interface extensions

  • IMPORT statement
  • abstract interfaces
  • procedure pointers

IMPORT statement

What:
The IMPORT statement is provided to import entities into interface bodies from their containing program-unit.
Why:
Otherwise such entities must be put into a separate module, which takes up global name space and exposes internal details (e.g. preventing a type from being opaque); and
type-bound procedures with function dummy arguments that return the type are not possible.
Syntax:
import-stmt ::= IMPORT [ :: ] entity-name-list
Constraints:
Such entities must have been previously declared (this avoids circular definitions).

Example:

  IMPORT :: derived_type, working_precision_kind

IMPORT statement example

  MODULE my_real_module
    TYPE my_real
      PRIVATE
      ...
    END TYPE
  CONTAINS
    FUNCTION integrate(fun,from,to)
      TYPE(my_real) integrate,from,to
      INTENT(IN) from,to
      INTERFACE
        FUNCTION fun(x)
          IMPORT my_real
          TYPE(my_real) fun,x
          INTENT(IN) x
        END
      END INTERFACE
      ...
    END FUNCTION
  END

Without IMPORT, either

  • TYPE my_real would need to be in a separate module (and could not be opaque if access to the components were needed inside my_real_module); or
  • TYPE my_real would need to be a SEQUENCE type (and without PRIVATE parts), and the definition would need to be repeated inside the interface for fun; or
  • fun would have to have an implicit interface instead of an explicit one, obstructing error detection.

Abstract interfaces

  • a name given to a procedure interface
  • used to declare procedures with that interface

ABSTRACT INTERFACE
  REAL FUNCTION fun_R_x_R_to_R(x,y)
    REAL,INTENT(IN) :: x,y
  END
END INTERFACE

PROCEDURE(fun_R_x_R_to_R) :: f1, f2

Back to the top

Procedure Pointers

A procedure with the POINTER attribute is a "procedure pointer". Note that this is not the same as a function result having the POINTER attribute.

  • modelled on dummy procedures.
  • can therefore have implicit interface.
  • can also have explicit interface.
  • may be structure components.
  • associated via pointer assignment.
  • the TARGET attribute is not necessary and is not allowed.
  • all procedures that can be actual arguments may be pointed to; thus not elemental procedures or generic procedures.

Procedure pointers: "implicit interface"

Example 1: An explicitly typed function.

  PROCEDURE(REAL),POINTER :: real_fun_pointer
  REAL,EXTERNAL :: f1
  ...
  real_fun_pointer => f1
  ...
  PRINT *,real_fun_pointer(1.5,2.0)
  PRINT *,f1(1.5,2.0)

Example 2: A subroutine or an implicitly typed function.

  PROCEDURE(),POINTER :: pp
  EXTERNAL :: sub
  ...
  pp => sub
  ...
  CALL pp('hi')

Procedure pointers: "explicit interface"

Example 1:

   PROCEDURE(fun_R_x_R_to_R),POINTER :: fptr

   fptr => f2

Example 2:

  ABSTRACT INTERFACE
    SUBROUTINE cbproc
    END
  END INTERFACE

  TYPE callback_list
    PROCEDURE(cbproc),NOPASS,POINTER :: callback
    TYPE(callback_list),POINTER :: next
  END TYPE

  • NOPASS specifies that the object through which the (callback) procedure is invoked is not to be passed to that procedure as an actual argument.

PROCEDURE statement: continued

This statement can also specify:

  • INTENT - for dummy procedure pointers;
  • OPTIONAL - for dummy procedures (including dummy procedure pointers);
  • SAVE - for non-dummy procedure pointers;
  • initialisation - ditto.

E.g. To save and initialise a procedure pointer:

  PROCEDURE(REAL),POINTER,SAVE :: p => NULL()

This statement may also be used to declare ordinary implicit-interface procedures:

  ! Equivalent to "REAL,EXTERNAL :: f"
  PROCEDURE(REAL) :: f
  !
  ! Equivalent to "EXTERNAL s"
  PROCEDURE() :: s

Dynamic (deferred) type parameters

  • only for nonKIND type parameters (e.g. for character length)
  • like deferred array specs
  • must be pointer or allocatable

NOTE: Extension - ALLOCATABLE scalars.

CHARACTER(:),POINTER :: p
READ *,n
ALLOCATE(CHARACTER(n)::p)
READ *,p
...
DEALLOCATE(p)

TYPE myvarchar
  CHARACTER(:),ALLOCATABLE :: value
END TYPE

New attributes

PROTECTED:
Access module variables without being able to change them.

VALUE:
Call-by-value semantics.

VOLATILE:
Completely disable optimisation.

Also:

ASYNCHRONOUS - for asynchronous i/o.

BIND - for C interoperability.

PROTECTED example

MODULE m
  INTEGER,PROTECTED,PUBLIC :: count_calls
CONTAINS
  SUBROUTINE call
    count_calls = count_calls + 1
  END SUBROUTINE
END
PROGRAM protected_example
  USE m
  ...
  PRINT *,count_calls  ! ok
  count_calls = 10     ! not ok

  • PROTECTED is allowed for a pointer, in which case it is the association status that is protected, not the value.
  • PROTECTED is not allowed for a procedure pointer, but seems to be allowed for an object containing a procedure pointer. This is probably a mistake!
  • Outside of the defining module, a PROTECTED variable has the same limitations on it as an INTENT(IN) dummy argument.
  • Saves having to create "access functions" which simply return the value of a PRIVATE variable.

VALUE example

PROGRAM value_example
  REAL :: x = 3
  CALL s(x)
  PRINT *,x           ! x is still 3
CONTAINS
  SUBROUTINE s(q)
    REAL,VALUE :: q
    q = q + 1         ! Alters q without altering x
    PRINT *,q         ! q is now 4
  END SUBROUTINE
END

  • The VALUE attribute is only allowed for scalar, non-pointer, non-allocatable dummy arguments.
  • INTENT(IN) may be specified as well as VALUE, but INTENT(INOUT) and INTENT(OUT) may not be.
  • For CHARACTER type, this is only allowed if the character length is one.
  • The primary motivation for this attribute is interfacing to other languages - such as C - which pass arguments by value.

VOLATILE example

  CHARACTER,TARGET,VOLATILE :: keystroke
  CALL set_receive_keystrokes(keystroke)
  DO i=1,1000
    IF (keystroke==ACHAR(3)) EXIT
    CALL some_lengthy_calculation(PART=i)
  END DO
  IF (i==1001) THEN
    PRINT *,'Calculation complete'
  ELSE
    PRINT *,'Exited via Ctrl-C'
  END IF

Note: A major motivation for VOLATILE was to be able to use certain MPI-2 calls.
Unfortunately these are much too complicated for an example!

Pointer extensions INTENT:
Controls changes to association status (not definition status).

Lower Bounds:
May be specified on pointer assignment.

Rank Remapping:
Change rank in pointer assignment.

POINTER INTENT

  SUBROUTINE pex(p1,p2,p3)
    INTENT(IN) :: p1
    INTENT(INOUT) :: p2
    INTENT(OUT) :: p3
    ...
    p1 = 2   ! ok
    p1 => p2 ! not permitted
  END

Notes:

  • p1 cannot have its association status altered during execution of pex, except that it may become undefined if its target is deallocated (through some other pointer).
  • p2 and p3 must be associated with pointer variables, not pointer function references.
  • p3 has undefined association status on entry to pex.

POINTER Lower Bounds

Lower bounds may be specified on pointer assignment.

  REAL,POINTER :: a(:),b(:),c(:)
  ...
  ALLOCATE(a(-10:10))    ! Lower bound of A is -10
  b => a                 ! Lower bound of B is -10
  c => a(-5:5)           ! Lower bound of C is 1
  c(-5:) => a(-5:5)      ! Lower bound of C is -5

The upper bounds are derived from the specified lower bounds and the extent.

POINTER Rank Remapping

Motivation: ability to have pointers to diagonals of matrices.

  REAL,ALLOCATABLE,TARGET :: base_array(:)
  REAL,POINTER :: matrix(:,:)
  REAL,POINTER :: diagonal(:)
  ...
  ALLOCATE(base_array(n*n))
  matrix(1:n,1:n) => base_array ! rank remapping
  diagonal => base_array(::n+1)

Notes:

  • The base array must be rank one, to ensure that the remapping is a simple linear transformation.
  • Both lower bound and upper bound must be specified for each dimension.

Back to the top

Array Constructor Extensions

Motivation:

  • Improve array constructor readability.
  • To make CHARACTER array constructors more usable.
  • To avoid ambiguity and complexity with CHARACTER zero-sized array constructors.

Extensions:

  1. Allow square brackets to delimit array constructors.
  2. Type specification of array constructors.

Array Constructors continued

Type specification syntax:

(/ type-spec :: ... /)
or
[ type-spec :: ... ]

Examples:

  [ REAL :: ]                 ! zero-sized array
  (/ (3.5,i=1,0) /)           ! F95 version

  [ CHARACTER(n) :: ]         ! zero-sized array
  (/ (repeat('x',n),i=1,0) /) ! F95 version dubious

  [ CHARACTER(50) :: 'a', 'bcdef' ] ! padded

Initialisation expressions: anything goes

Initialisation expressions are used for:

  • Values for PARAMETERs, e.g. PARAMETER(N=20)
  • KIND specifiers, e.g. 2.0_WP
  • KIND type parameters, e.g. REAL(WP) X,Y

In Fortran 95, these expressions can only use INTEGER, LOGICAL and CHARACTER intrinsic functions, not REAL functions like ATAN.

In Fortran 2000, these expressions can use any intrinsic function.

  !
  ! Probably a poor approximation to pi.
  !
  REAL,PARAMETER :: pi = 4*atan(1.0)

MAX and MIN for character type

The MAX and MIN intrinsic functions have been extended to operate on values of type CHARACTER.

  CHARACTER(*) x,y,z
  ...
  PRINT *,max(x,y,z)

These intrinsics use native character set ordering (as provided by <, <=, >, >=), not the ASCII character set ordering (as provided by LLT, LLE, LGT and LGE).

"Enhanced" complex constants

In previous Fortrans, complex literal constants have optionally signed integer and real literal constants for the real and imaginary parts.

In Fortran 2000, the real and imaginary parts may also be named constants (PARAMETERs).

Old style:

  REAL,PARAMETER :: pi = 22/7
  COMPLEX c
  c = cmplx(pi,-pi)

New style:

  REAL,PARAMETER :: pi = 22/7
  REAL,PARAMETER :: minuspi = -pi
  COMPLEX c
  c = (pi,minuspi)

  • That's right, the real or imaginary part may be a named constant, but no sign is allowed.

ASSOCIATE construct

The ASSOCIATE construct allows the association of a complicated variable or expression with a simple name (somewhat like the Pascal "with" construct).

The "associate-name" is definable (i.e. like a variable) only if it is associated with a variable.

  ASSOCIATE(x=>cos(phi/2)*sin(eta),j=>mod(i,3))
    PRINT *,x
    ASSOCIATE(y=>a(ifun())%b(j))
      y%n = 0
      y%value = y%value + x
    END ASSOCIATE
    x = 0    ! This is not allowed
  END ASSOCIATE

Expressions in the ASSOCIATE statement are evaluated once only, at the start of the associate block.

Mixed component accessibility

With type extension (q.v.), the situation naturally arises where some components are private and others are public.

For consistency, it is therefore possible to explicitly specify the accessibility at a per-component level.

  TYPE t
    REAL,PUBLIC :: accessible_component
    REAL,PRIVATE :: inaccessible_component
  END TYPE

Public entities of private type

In Fortran 95, entities of a PRIVATE type must themselves be private.

There is no technical reason for this; other situations can arise when an entity is visible but its type is not (e.g. particularly with host association).

In Fortran 2000, this restriction was deemed to be unnecessary and is lifted.

Public entities of private type ...

An example of the feature is to provide constants which can be used as actual arguments, but since the type is private, cannot be assigned to variables.

MODULE m
  TYPE,PRIVATE :: t
    INTEGER hiddenvalue
  END TYPE
  TYPE(t),PUBLIC :: action_1 = t(1)
  TYPE(t),PUBLIC :: action_2 = t(2)
CONTAINS
  SUBROUTINE act(action)
    TYPE(t) action
    ...
  END SUBROUTINE
END

Type Aliases

  • A type alias provides a name for a type-spec.
  • Type alias names share the derived-type name space; indeed, in use it looks like a derived type.
  • A type alias can be used everywhere a type-spec can, and has approximately the same effect as repeating the type specification would have.

  TYPEALIAS :: byte=>INTEGER(1), single=>REAL(1)
  TYPE(byte) b
  TYPE(single) f

The type parameters in a TYPEALIAS statement must be initialisation expressions (i.e. constant).

  SUBROUTINE s(ch,n)
    TYPEALIAS :: assumed_char=>CHARACTER(*) ! Bad
    TYPEALIAS :: varchar=>CHARACTER(n)      ! Bad

More Type Aliasing

A type alias can be used to switch between intrinsic types and derived types with a single edit:

  ! Uncomment one of the following:
  ! TYPEALIAS :: wreal => REAL
  ! TYPEALIAS :: wreal => DOUBLE PRECISION
  ! TYPEALIAS :: wreal => TYPE(very_big_reals)

A typealias is a name for a type-spec, not a type; thus it has no type parameters of its own.

  TYPE(single(2)) double_variable  ! Not ok

For much the same reason, one cannot extend a type alias, even if it is an alias for an extensible type (q.v.).

  TYPEALIAS :: alias => extensible_type
  TYPE,EXTENDS(alias) :: newtype   ! Not ok

Enumeration Types

  • Modelled after C enum specifier.
  • May be "C-interoperable".
  • It is a type alias for some integer type.
  • Not a new type, thus unsafe.

  ENUM :: colour
    ENUMERATOR :: red,orange=1000,yellow=2,green
  END ENUM
  ...
  TYPE(colour) paintwork
  paintwork = miles/gallon ! ok!

The enumerator values are obtained by a simple algorithm: thus red==0 and green==3.

More Enumeration Types

One can specify the integer kind instead of letting the compiler pick it:

  ENUM(KIND=selected_int_kind(2)) bite
  ...

One can request C interoperability:

  ENUM,BIND(C) :: c_enum
  ...

C interoperability really means requesting the compiler to use the same algorithm for picking the integer kind that the C compiler would use for the same enumerator list (viz same values in the same order).

Back to the top


Summary

  • allocatable components/dummies/functions
  • import into interface body
  • abstract interfaces
  • procedure pointers
  • protected module variables
  • value dummy arguments
  • volatile variables
  • pointer intent
  • pointer lower bounds
  • pointer rank remapping
  • array constructor niceness
  • stronger initialisation expressions
  • MAX and MIN for CHARACTER
  • ASSOCIATE construct
  • Enhanced complex constants
  • Mixed component accessibility
  • Public entities of private type
  • Type aliases
  • Enum construct


Valid HTML 4.01! Comments on this or any other of the Group's pages should be sent by e-mail to the BCS FSG Web Editor, Peter Crouch, at pccrouch@bcs.org.uk



© Copyright The British Computer Society