NAG Fortran Compiler, Release 7.2: Object-oriented programming

Navigation: Previous Up Next

## 9.2 Object-oriented programming

### 9.2.1 Type Extension

Type extension provides the first phase of object orientation: inheritance and polymorphic objects.

#### 9.2.1.1 Extending Types [5.0]

Any derived type can be extended using the EXTENDS keyword, except for SEQUENCE types and BIND(C) types. (The latter types are “non-extensible”, as are intrinsic types, whereas all other derived types are “extensible”.) The extended type inherits all the components of the parent type and may add extra components.

For example:

      TYPE point
        REAL x,y
      END TYPE
      TYPE,EXTENDS(point) :: point_3d
        REAL z
      END TYPE

The type `point_3d` has `x`, `y` and `z` components. Additionally, it has a `point` component which refers to the inherited part; this “parent component” is “inheritance-associated” with the inherited components, so that the `point%x` component is identical to the `x` component et cetera.

However, when extending a type it is not required to add any new components; for example,

      TYPE,EXTENDS(point) :: newpoint
      END TYPE

defines a new type `newpoint` which has exactly the same components as point (plus the associated parent component). Similarly, it is no longer necessary for a type to contain any components:

      TYPE empty_type
      END TYPE

declares the extensible (but not extended) type `empty_type` which has no components at all.

#### 9.2.1.2 Polymorphic Variables [5.0]

A polymorphic variable is a pointer, allocatable array or dummy argument that is declared using the CLASS keyword instead of the TYPE keyword. A `CLASS(`typename`)` variable can assume any type in the class of types consisting of `TYPE(`typename`)` and all extensions of typename.

For example:

      REAL FUNCTION bearing(a)
        CLASS(point) a
        bearing = atan2(a%y,a%x)
      END

The function `bearing` may be applied to a `TYPE(point)` object or to a `TYPE(point_3d)` object, or indeed to an object of any type that is an extension of `TYPE(point)`.

#### 9.2.1.3 Type Selection [5.0]

The `SELECT TYPE` construct provides both a means of testing the dynamic type of a polymorphic variable and access to the extended components of that variable.

For example:

      CLASS(t) x
      ...
      SELECT TYPE(p=>x)
      TYPE IS (t1)
        !
        ! This section is executed only if X is exactly of TYPE(t1), not an
        ! extension thereof.  P is TYPE(t1).
        !
      TYPE IS (t2)
        !
        ! This section is executed only if X is exactly of TYPE(t2), not an
        ! extension thereof.  P is TYPE(t2).
        !
      CLASS IS (t3)
        !
        ! This section is executed if X is of TYPE(t3), or of some extension
        ! thereof, and if it is not caught by a more specific case.  P is CLASS(t3).
        !
      END SELECT

Note that ‘`SELECT TYPE(x)`’ is short for ‘`SELECT TYPE(x=>x)`’.

#### 9.2.1.4 Unlimited polymorphism [5.2]

A variable that is ‘`CLASS(*)`’ is an unlimited polymorphic variable. It has no type, but can assume any type including non-extensible types and intrinsic types (and kinds). Apart from allocation, deallocation and pointer assignment, to perform any operation on an unlimited polymorphic you first have to discover its type using `SELECT TYPE`. For example:

      CLASS(*),POINTER :: x
      CHARACTER(17),TARGET :: ch
      x => ch
      SELECT TYPE(x)
      TYPE IS (COMPLEX(KIND=KIND(0d0)))
        PRINT *,x+1
      TYPE IS (CHARACTER(LEN=*))
        PRINT *,LEN(x)
      END SELECT

Note that in the case of `CHARACTER` the length must be specified as ‘`*`’ and is automatically assumed from whatever the polymorphic is associated with.

In the case of a non-extensible (i.e. `BIND(C)` or `SEQUENCE`) type, `SELECT TYPE` cannot be used to discover the type; instead, an unsafe pointer assignment is allowed, for example:

      TYPE t
        SEQUENCE
        REAL x
      END TYPE
      CLASS(*),POINTER :: x
      TYPE(t),POINTER :: y
      ...
      y => x ! Unsafe - the compiler cannot tell whether X is TYPE(t).

#### 9.2.1.5 Ad hoc type comparison [5.3]

Two new intrinsic functions are provided for comparing the dynamic types of polymorphic objects. These are

`EXTENDS_TYPE_OF(A,MOLD)`
`SAME_TYPE_AS(A,B)`

The arguments must be objects of extensible types (though they need not be polymorphic). `SAME_TYPE_AS` returns `.TRUE.` if and only if both `A` and `B` have the same dynamic type. `EXTENDS_TYPE_OF` returns `.TRUE.` if and only if the dynamic type of `A` is the same as, or an extension of, the dynamic type of `MOLD`. Note that if `MOLD` is an unallocated unlimited polymorphic (`CLASS(*)`), the result will be true regardless of the state of `A`.

The arguments are permitted to be unallocated or disassociated, but they are not permitted to be pointers with an undefined association status.

It is recommended that where possible these intrinsic functions be avoided, and that `SELECT TYPE` be used for type checking instead.

### 9.2.2 Typed allocation [5.1]

The `ALLOCATE` statement now accepts a type-spec; this can be used to specify the dynamic type (and type parameters, if any) of an allocation. The type-spec appears before the allocation list, and is separated from it by a double colon.

For example, if `T` is an extensible type and `ET` is an extension of `T`,

        CLASS(t),POINTER :: a(:)
        ALLOCATE(et::a(100))

allocates `A` to have dynamic type `ET`. Note that the type-spec in an `ALLOCATE` statement omits the `TYPE` keyword for derived types, similarly to the `TYPE IS` and `CLASS IS` statements.

An unlimited polymorphic object can be allocated to be any type including intrinsic types: for example

        CLASS(*),POINTER :: c,d
        ALLOCATE(DOUBLE PRECISION::c)
        READ *,n
        ALLOCATE(CHARACTER(LEN=n)::d)

allocates `C` to be double precision real, and `D` to be of type `CHARACTER` with length `N`.

Typed allocation is only useful for allocating polymorphic variables and `CHARACTER` variables with deferred length (`LEN=:`). For a non-polymorphic variable, the type-spec must specify the declared type and, if it is type `CHARACTER` but not deferred-length, to have the same character length. The character length must not be specifed as an asterisk (`CHARACTER(LEN=*)`) unless the allocate-object is a dummy argument with an asterisk character length (and vice versa).

Finally, since there is only one type-spec it must be compatible with all the items in the allocation list.

### 9.2.3 Sourced allocation (cloning) [5.1]

The `ALLOCATE` statement now accepts the `SOURCE=` specifier. The dynamic type and value of the allocated entity is taken from the expression in the specifier. If the derived type has type parameters (q.v.), the value for any deferred type parameter is taken from the source expression, and the values for other type parameters must agree. This is not just applicable to derived types: if the entity being allocated is type `CHARACTER` with deferred length (`LEN=:`), the character length is taken from the source expression.

Only one entity can be allocated when the `SOURCE=` specifier is used. Note that when allocating an array the array shape is not taken from the source expression but must be specified in the usual way. If the source expression is an array, it must have the same shape as the array being allocated.

For example,

        CLASS(*),POINTER :: a,b
        ...
        ALLOCATE(a,SOURCE=b)

The allocated variable `A` will be a “clone” of `B`, whatever the current type of `B` happens to be.

### 9.2.4 Type-bound procedures [5.1]

Type-bound procedures provide a means of packaging operations on a type with the type itself, and also for dynamic dispatch to a procedure depending on the dynamic type of a polymorphic variable.

#### 9.2.4.1 The type-bound procedure part

The type-bound procedure part of a type definition is separated from the components by the `CONTAINS` statement. The default accessibility of type-bound procedures is public even if the components are private; this may be changed by using the `PRIVATE` statement after the `CONTAINS`.

#### 9.2.4.2 Specific type-bound procedures

The syntax of a specific, non-deferred, type-bound procedure declaration is:

`PROCEDURE` [[,binding-attr-list]::] binding-name [`=>`procedure-name]

The name of the type-bound procedure is binding-name, and the name of the actual procedure which implements it is procedure-name. If the optional `=>`procedure-name is omitted, the actual procedure has the same name as the binding.

A type-bound procedure is invoked via an object of the type, e.g.

      CALL variable(i)%tbp(arguments)

Normally, the invoking variable is passed as an extra argument, the “passed-object dummy argument”; by default this is the first dummy argument of the actual procedure and so the first argument in the argument list becomes the second argument, etc. The passed-object dummy argument may be changed by declaring the type-bound procedure with the `PASS(`argument-name`)` attribute, in which case the variable is passed as the named argument. The `PASS` attribute may also be used to confirm the default (as the first argument), and the `NOPASS` attribute prevents passing the object as an argument at all. The passed-object dummy argument must be a polymorphic scalar variable of that type, e.g. `CLASS(t) self`.

When a type is extended, the new type either inherits or overrides each type-bound procedure of the old type. An overriding procedure must be compatible with the old procedure; in particular, each dummy argument must have the same type except for the passed-object dummy argument which must have the new type. A type-bound procedure that is declared to be `NON_OVERRIDABLE` cannot be overridden during type extension.

When a type-bound procedure is invoked, it is the dynamic type of the variable which determines which actual procedure to call.

The other attributes that a type-bound procedure may have are `PUBLIC`, `PRIVATE`, and `DEFERRED` (the latter only for abstract types, which are described later).

#### 9.2.4.3 Generic type-bound procedures

A generic type-bound procedure is a set of specific type-bound procedures, in the same way that an ordinary generic procedure is a set of specific ordinary procedures. It is declared with the `GENERIC` statement, e.g.

      GENERIC :: generic_name => specific_name_1, specific_name_2, specific_name_3

Generic type-bound procedures may also be operators or assignment, e.g.

      GENERIC :: OPERATOR(+) => add_t_t, add_t_r, add_r_t

Such type-bound generic operators cannot have the `NOPASS` attribute; the dynamic type of the passed-object dummy argument determines which actual procedure is called.

When a type is extended, the new type inherits all the generic type-bound procedures without exception, and the new type may extend the generic with additional specific procedures. To override procedures in the generic, simply override the specific type-bound procedure. For example, in

      TYPE mycomplex
        ...
      CONTAINS
        PROCEDURE :: myc_plus_r => myc1_plus_r
        PROCEDURE,PASS(B) :: r_plus_myc => r_plus_myc1
        GENERIC :: OPERATOR(+) => myc_plus_r, r_plus_myc
      END TYPE
      ...
      TYPE,EXTENDS(mycomplex) :: mycomplex_2
        ...
      CONTAINS
        PROCEDURE :: myc_plus_r => myc2_plus_r
        PROCEDURE,PASS(B) :: r_plus_myc => r_plus_myc2
      END TYPE

the type `mycomplex_2` inherits the generic operator ‘`+`’; invoking the generic (`+`) invokes the specific type-bound procedure, which for entities of type `mycomplex_2` will invoke the overriding actual procedure (`myc2_plus_r` or `r_plus_myc2`).

### 9.2.5 Abstract derived types [5.1]

An extensible derived type can be declared to be `ABSTRACT`, e.g.

      TYPE, ABSTRACT :: mytype

An abstract type cannot be instantiated; i.e. it is not allowed to declare a non-polymorphic variable of abstract type, and a polymorphic variable of abstract type must be allocated to be a non-abstract extension of the type.

Abstract type may contain `DEFERRED` type-bound procedures, e.g.

      ...
      CONTAINS
        PROCEDURE(interface_name),DEFERRED :: tbpname

No binding (“`=> name`”) is allowed or implied by a deferred procedure binding. The `interface_name` must be the name of an abstract interface or a procedure with an explicit interface, and defines the interface of the deferred type-bound procedure.

When extending an abstract type, the extended type must also be abstract unless it overrides all of the deferred type-bound procedures with normal bindings.

### 9.2.6 Object-bound procedures [5.2]

These are procedure pointer components, and act similarly to type-bound procedures except that the binding is per-object not per-type. The syntax of a procedure pointer component declaration is:

`PROCEDURE(` [proc-interface] `) ,` proc-component-attr-spec-list :: proc-decl-list

where

- each proc-component-attr-spec is one of `NOPASS`, `PASS`, `PASS(`arg-name`)`, `POINTER`, `PRIVATE` or `PUBLIC`, and
- each proc-decl is a component name optionally followed by default initialisation to null (‘`=> NULL()`’).

The `POINTER` attribute is required.

Note that object-bound procedures have a passed-object dummy argument just like type-bound procedures; if this is not wanted, the `NOPASS` attribute must be used (and this is required if the interface is implicit, i.e. when `proc-interface` is missing or is a type specification).

The following example demonstrates using a list of subroutines with no arguments.

      TYPE action_list
        PROCEDURE(),NOPASS,POINTER :: action => NULL()
        TYPE(action_list),POINTER :: next
      END TYPE
      TYPE(t),TARGET :: top
      TYPE(t),POINTER :: p
      EXTERNAL sub1,sub2
      top%action = sub1
      ALLOCATE(top%next)
      top%next%action = sub2
      ...
      p => top
      DO WHILE (ASSOCIATED(p))
        IF (ASSOCIATED(p%action)) CALL p%action
        p => p%next
      END DO