Sei sulla pagina 1di 63

Fortran 95: Building blocks

for object-based programming


Fortran Type System:
Intrinsic types (1)

Use KIND instead


Which ones are available?
1. integer
integer, parameter :: ik = &
2. real   selected_int_kind(6)
3. complex integer(ik) :: myint
4. logical :
5. character myint = 42_ik

Declaration: 6 6
−10 myint10
(minimum range)
integer myint
compiler barfs if KIND
double precision y
number unavailable
but beware constants
works OK, but not change only one line to
recommended convert all objects
implementation dependency encapsulation for re-use also
possible
©2006-8 LRZ 2
Fortran Type System:
Intrinsic types (2)

KINDS of reals

integer, parameter :: rk = selected_real_kind(12,100)
real(rk) :: y
:
y = 3.14_rk

first argument „p“: decimal precision


second argument „r“: decimal exponent range
one of two arguments can be omitted
real KIND numbers also apply to each component of type 
complex
KINDS of logical, character
not defined
UNICODE support however in
©2006-8 LRZ 3
Fortran Type System: C++ STL functionality
Arrays
Arrays of intrinsic type Performance feature
static implement as reference to
contiguous chunk of
integer, parameter :: dim = 100 memory
real(rk), dimension(dim) :: y_a knowledge of layout
: optimize accesses
y_a(12:98:3) = (/ ... /) usually in (nested loop) access
patterns
elements 12, 15, 18, ..., 96 are loop unrolling, blocking, ... by
assigned an array expression compiler
dynamic Multi-dimensional arrays
real(rk), allocatable, & up to 7 dimensions
  dimension(:) :: y_a innermost index
: contiguous
allocate(y_a(dim), stat=...)
: ! check status and use y_a(:)
deallocate(y_a)
©2006-8 LRZ 4
Fortran Type System:
Handling Strings

While it is possible to define an array of characters

character, dimension(20) :: line

for handling of strings length parametrization is more appropriate:

character(len=20) :: line ! this is a scalar

disadvantage: no dynamic adjustment possible. Hence,

line = 'give me a very long line' ; write(6, *) line

will print 'give me a very long ', and


line(:) = 'a short line' ; write(6, *) trim(line)

will print 'a short line' (trim will remove trailing white spaces)


Reference a substring: line(i:j), line(:), line(i:), ...

©2006-8 LRZ 5
Fortran Type System
Derived types (1)

Type definition: Structure declaration


„instantiation“
type :: body uses „real“ memory
 real :: mass
 character(len=5) :: units type(body) :: earth
 real, dimension(3) :: pos type(body), allocatable :: &
 real, dimension(3) :: vel              planets(:)
end type
earth: static scalar
planets: dynamic array
improved abstraction
group data belonging to a concept Note:
together type must be defined
before instantiation
aggregation allowed:
must be in scope of type
intrinsic types
definition
previously defined derived types scope ~ „region of validity“

©2006-8 LRZ 6
Fortran Type System
Derived types (2)

Default initialization Accessing components


within type definition write access:
improved version -
earth%mass = 5.5e18
type :: body     earth%units = 'mks  '
 real(rk) :: mass = 0._rk
 character(5) :: &
       units = 'mks  ' read access analogous:
 real(rk), dimension(3) :: &
       pos = (/ 3 * 0._rk /) real(rk) :: em
 real(rk), dimension(3) :: &
    :
       vel = (/ 3 * 0._rk /)
end type     em = earth%mass
component
recommended practice selector

©2006-8 LRZ 7
Fortran Type System
Derived types (3)

Assignment
default is to copy all components -
type (body) :: earth
type (body), allocatable :: planets(:)
:
earth%mass = ...
: ! initialize all components
allocate(planets(8))
planets(3) = earth
nested derived types are treated recursively
see later slide
types with dynamic components need special attention
treated in a separate section of this talk

©2006-8 LRZ 8
Fortran Type System
Derived types (4)

Structure constructor: also known as


earth = body(5.5e18_rk, & „object composition“
'mks',1.5e11_rk, ...) Accessing type
again: beware dynamic components:
components type (charged_body) :: x
:
Nested type definition: x%m%mass = 1.2
type charged_body x%m%units = 'cgs'
type (body) :: m x%charge = 1.4e-6
real(rk) :: charge tiresome for many
end type levels of nesting
„has a“ relationship:
charged_body has a
body
©2006-8 LRZ 9
Arrays of derived types

Looking at planets(:) array However, two subpart


Extract scalar subobjects references are not allowed
need to use loop:
character(len=5) :: txt
real :: pmass real :: all_pvel(3,8)
: :
txt = planets(3)%units do i = 1, 8
pmass = planets(4)%mass   pvel(:,i) = &
          planets(i)%vel(:)
substring refs allowed
end do
Extract array subobjects ! ... = planets(:)%vel(:)
real :: pvel(3) ! is illegal
real :: xvel(4)
note storage layout
:
pvel(:) = planets(5)%vel(:) Also works for
xvel(:) = & derived type components
        planets(1:8:2)%vel(1) dynamical components

©2006-8 LRZ 10
Manipulating array sections:
Vector subscripts and the cshift() intrinsic

Extract (unordered) subset real :: v(20), vs(20)


real :: w(10,20), ws(10, 20)
type(body) :: p_subset(4)
:
vs = cshift(v, 3)
p_subset(:) = &
ws = cshift(w, 3, 2)
     planets( (/4,3,9,3/) )

Can be ... v
injective
many-one (as above) ... vs
Restrictions:
many-one not on left hand consistency of type and
side of an assignment shape required
actual subroutine can also specify an array
argument: dummy may not of integers as second
be intent(out|inout) argument

©2006-8 LRZ 11
What can we do with all this?

First, some caveats Additional wishes:


type definition only (especially if you know C++)
usable within scope of manipulate type
defining program unit components through API
cannot re-declare same type calls („methods“)
what about re-use? make type components
inaccessible otherwise
special role of type
(„encapsulation“)
definition with sequence
attribute An additional kind of
determine storage layout program unit is required
can be re-defined elsewhere known kinds: program,
procedure
loss of type safety
general recommendation: do not
use

©2006-8 LRZ 12
Fortran Module concept:
Definition

A module may contain: Graphical representation:


type definitions „OMT extended for
declarations of objects modules“
declarations of interfaces
ocean module name
data encapsulation
attributes and statements public objects
(private, public) and
these constrain accessibility to type definitions
module-defined entities from
outside the module ocean_init_t() public
ocean_set_t() procedures
subroutines and functions
...
(„module procedures“)
namelist groups

note rounded corners of box

©2006-8 LRZ 13
Fortran Module concept:
Example: Storage Pool (1)

Realization of diagram from previous slide

module ocean
  implicit none
  real, private, save, allocatable :: ocean_t(:,:,:) 
contains
  subroutine ocean_init_t(n) temperature
    integer, intent(in) :: n(3)
    allocate(ocean_t(n(1),n(2),n(3))) ! check status etc.omitted
  end subroutine ocean_init_t
  subroutine ocean_set_t(i,j,k,val)
    integer, intent(in) :: i,j,k
    real, intent(in) :: val 
    ocean_t(i,j,k) = val
  end subroutine ocean_set_t
! analogous: ocean_get_t() and further calls
end module ocean

©2006-8 LRZ 14
Fortran Module concept
Storage pool (2): Explanations

properties of storage area: contained module


private attribute procedures:
encapsulates access possible to all
save attribute guarantees objects and data types
survival of ocean_t even →„host association“
if scope is left automatically define an
default attribute is public „explicit interface“
all subroutines
optimization aspect:
assume evaluation of
each matrix element
expensive compared to
storing/reading

©2006-8 LRZ 15
Fortran Module concept: ocean

Using the module (1)


ocean_init_t()
ocean_set_t()
program myprog ...

use ocean „use inheritance“


implicit none u

real :: x
myprog
:
call ocean_init_t((/34,34,22/))
ocean_t(3,6,2) = 1.4 ! will fail to compile
call ocean_set_t(3,6,2,1.4) ! this is OK
call ocean_set_t(3,6,2.0,1.4) ! will fail to compile
call ocean_get_t(3,6,2,x)
write(6, *) x ! will write 1.4 to unit 6
:
end program
©2006-8 LRZ 16
Fortran Module concept:
Using the module (2)

other program units: 1. programs


may access all publically 2. subroutines
available entities can be module
→ „use association“ procedures in other
as if redeclared module
only way to re-use type definitions 3. other modules
no (indirect) circular
use statement requires
module references
pre-compiled module information
this is enforced by
for argument module(s)
separate compilation
compiler checks
referenced entities
→ form directed acyclical
graph
consistent use of explicit
interfaces

©2006-8 LRZ 17
Fortran Module concept:
A technical aspect

Handling the compilation:


generic f90 command assumed
1. compile ocean: f90 ­c ­o ocean.o ocean.f90
2. compile myprog: f90 ­c myprog.f90
3. link to an executable program
f90 ­o myprog.exe myprog.o ocean.o
compiling ocean.f90 generates a module information file
typically called ocean.mod
needed to compile myprog.f90 (access entities, check against explicit interface)
module information file is
compiler specific, hopefully designed for lookup efficiency
usually in proprietary format (name mangling)
no binary compatibility between compilers and platforms
not a library file (instead, think „precompiled header“)

©2006-8 LRZ 18
Fortran Module concept:
Module procedures as methods (1)

Add procedures to a type


definition

module mod_foo function foo_create(this,...)
  implicit none     type(foo), ... :: this
  private     :
  public :: foo_create, & end function foo_create 
            foo_destroy, & subroutine foo_destroy(...)
            foo_xxx     :
  type, public :: foo end subroutine
    private  function foo_xxx(x) result(y)
    integer :: len     type(foo), ... :: x
    real, ... :: stuff     logical :: y
  end type     :
contains   end function foo_xxx
! continued to the right   end module foo

©2006-8 LRZ 19
Fortran Module concept:
Module procedures as methods (2)

encapsulation: C++ class definition


no access to type include file
components outside
module host scope #include <iostream>
enforce use of (accessor) #include <iomanip>
methods #include <string>
object-based program class Foo {
function return value:  public:
    Foo() : len_(0),stuff_(NULL) {};
specify via result
    Foo(int, float *);
is not a dummy argument     ~Foo();
contrast to C++     virtual void print();
 protected:
encapsulation on level of     int len_;
module, not „class“  private:
no default methods (e.g.,     float *stuff_;
constructor) };

©2006-8 LRZ 20
Fortran Module concept:
diagrammatic representation

Fortran 95 C++ (inheritance)


Foo Foo2
mod_foo
len_ name_
* stuff_
foo foo2
Foo() Foo2()
len name : :
stuff(:)

foo_create() foo2_create() other virtual methods inherited


: :

class Foo2 : public Foo {
 private:
type, public :: foo2     string name_;
  private  public:
! aggregation of foo     Foo2() : name_(NULL) {};
  type(foo) :: foo     Foo2(int, float *, string &);
  character(len=10) :: name     virtual void print();
end type };

©2006-8 LRZ 21
Specifying intent:
Letting the compiler help you

function foo_create(this,y) argument becomes


  type(foo), & undefined on entry
        intent(inout) :: this beware unwritten components
  real, intent(in) :: y(n) beware memory leaks in objects
with pointer components
  integer :: foo_create
    : intent(inout):
end function foo_create similar to out, except:
may dereference before writing
intent(in):
no changes allowed to value
unwritten components keep value
from entry
intent(out):
do not dereference before
pointer argument
writing no intent allowed in
had better write before refers to pointer association
exiting procedure because ... status in

©2006-8 LRZ 22
Interfaces (1)

establish definition of interfaces for externally


communication mechanism defined routines
available to client code
categories of interface interface
explicit   subroutine stuff_1(x,...)
    type(...), ... :: x
implicit (Fortran 77 style)     :
→ do not use implicit   end subroutine stuff_1 
interfaces, compiler end interface
cannot check
generation of explicit
interfaces: some compilers allow to
automatic for contained auto-generate these from
module procedures Fortran 77 source
use interface block in ifort switch: ­gen­interfaces
module definition section

©2006-8 LRZ 23
Interfaces (2): Overloading and generics

module mod_foo
Situation:
  private
have (set of) various   public :: create
types
  : ! define public types foo, foo2
may be intrinsic or derived   interface create
have a well-defined set     module procedure &
of operations       foo_create, foo2_create 
working in the same manner for   end interface
each member of the set contains
uniform usability desired   function foo_create(x, ...)
    type(foo) :: x
define a generic interface
    :
overloads specific names
  end function
allows to use a single
  function foo2_create(x, ...)
method name for each
    type(foo2) :: x
member of the set
    :
e.g., static polymorphism (only
  end function
object argument varies)
end module mod_foo
©2006-8 LRZ 24
Interfaces (3): Using generic calls

program myprog generic interface also


required across modules
  use mod_foo
to prevent name collision
  use mod_3 ! def. type foo3
one specific per module
  type(foo) :: x may have same name as
  type(foo2) :: y generic
  type(foo3) :: z argument list:
  : must differ sufficiently to ensure
  call create(x,...) unambiguous invocation: „TKR“
  call create(y,...) 1. data type
  call create(z,...) 2. kind type parameter
3. rank
  :
end program in particular, pointer or
allocatable attributes do
specifics must be all sub-
not differentiate!
routines or all functions
role of optional arguments

©2006-8 LRZ 25
Diagrammatic representation
for generic calls

Italics to indicate generic-ness


replicate for each type which has specific implementation

mod_foo mod_3

create() create()

foo foo2 foo3


len name ?
stuff(:)

create() create() create()


: : :

©2006-8 LRZ 26
Interfaces (4): Operator overloading

assignment self defined operator


interface assignment(=) interface operator(.myop.)
  module procedure e1, e2     module procedure m1, m2
end interface
end interface
function with one or two
module procedures must be
intent(in) arguments
subroutines with two arguments
up to 31 letter characters
first with intent(out) or between dots
intent(inout), second with usage in expressions
intent(in) unary  .unary_op. y
extend existing operator binary    x .myop. y
interface operator(+) All these are generic
  module procedure p1, p2 Recommendation:
end interface do not overload assignment
function with one or two overload intrinsic (+, -, ...)
intent(in) arguments only if necessary
consistency with intrinsic and follow semantics!
©2006-8 LRZ 27
Interfaces (5): Optional arguments

Want to be able to omit arguments if not always needed


module mod_foo
 : ! omitted definitions
contains
  subroutine foo_calc(this, func, param) 
    type(foo), intent(inout) :: this 
    interface
      real function func(t, param)
        type(foo), intent(in) :: t
        type(par_type), intent(in) :: param
      end function
    end interface
    optional :: func
    type(par_type), intent(in), optional :: param
    : ! see next slide 
  end subroutine
end module     

©2006-8 LRZ 28
Interfaces (6): Optional arguments cont'd

present() intrinsic
Client use:
logical function
require keyword call
type(par_type) :: param_def starting with first
:  optional
param_def = ... ! set default
also for non-optionals coming
if (present(param)) then
  param_def = param after first optional
! may need overloaded ! possible calls are:
! assignment call foo_calc(this)
end if call foo_calc(this, func=f, param=p)
call foo_calc(this, param=p, func=f)
function parameters call foo_calc(this, param=p)
may not reference at all if call foo_calc(this, func=f)
not present
undefined
can hand on non-present suggests programming style: all
need for check only optionals to the end of the
postponed argument list

©2006-8 LRZ 29
Concluding remarks on explicit interfaces

usage of module procedures:


only those which have been made public
for generics, typically only the generic name should be made public
compiler checks correct use of interface
TKR of actual parameters
other characteristics: pointer, allocatable, intent
against definition
mandatory for most „advanced“ Fortran functionality
functions with array result
allocatable dummy arguments (see later)
pointer/target dummy arguments (see later)
intent
generic interfaces
optional arguments
use as extensively as possible!
©2006-8 LRZ 30
Programming Style Hints

Recommendations
for software engineering
when using modules
defining a naming scheme
default attributes to choose
handling global variables
elimination of COMMON and EQUIVALENCE
function / subroutine arguments

©2006-8 LRZ 31
Recommendations:
Use a naming scheme (1)
Module:
defines internal name space for compiler
typical mangling:  foo_MP_foo_create for method foo_create
name space separation mostly unavailable to client
same (global) name (type definition, object, module procedure) may occur multiple times,
but not be referenced via use association (only clauses and/or renaming can help –
see following slides)
compiler will refuse compilation or linking if this occurs
exceptions: generic interfaces / operator overloading

→ define consistent naming scheme for public symbols


need not be the same for each module
Example (module defining some type):
module name mod_foo
type name foo
method names foo_create
©2006-8 LRZ 32
Recommendations:
Use a naming scheme (2):

Handling of name clashes: alternative:


renaming of module one of the clashing symbols not needed
entities possible use only clause
 use mod_tt, only : foo
use mod_tt, t1 => tt also contributes to taking pressure off
use mod2_tt  namespace
type(t1) :: o_t1 self-defined operators
type(tt) :: o_tt
cannot be renamed in
this has been enabled for
tt defined by module use mod, operator(.locop.) & => 
mod2_tt a different                  operator(.myop.)
derived type with
(possibly) different
internal structure

©2006-8 LRZ 33
Recommendations:
What to put into every module
module xyz blanket save statement
use .. ! whatever needed may use in module
implicit none declaration section
save values of uninitialized globals otherwise
private not guaranteed to be preserved if
... module not used by main program
end module xyz better alternative:
use xyz, only :
remove implicit typing
in main program, or
compiler enforces strong
typing save attribute on objects, or
blanket private initialize objects
sets default accessibility but not in subroutines
all exported entities need use save attribute ...
explicit public attribute and use with care
beware side effects e.g., in recursive
subroutines!
©2006-8 LRZ 34
Global objects (1)
Example: counting objects
add global variable count C++: static member variable

module mod_foo class Foo {
  :  public:
  public :: ..., get_count      Foo() : ...;
  integer, private, save :: &     Foo(int, float *);
            count = 0     :
  :  protected:
contains     static int count;
  function foo_create(this,...)     int len_;
    :  private:
    count = count + 1     float *stuff_;        };
  end function foo_create
! destructor must decrement #include “Foo.h“
  integer function get_count() int Foo::count = 0;
    get_count = count
  end function Foo::Foo() {
  :    count += 1;
end module } 
// same with all other constr.

©2006-8 LRZ 35
Global variables (2) ... and don't forget to switch
Threading issues on OpenMP everywhere!

Example: Fix: named critical


naming reduces congestion
use OpenMP directives
  function foo_create(this,...)
type(foo) :: obj     :
: !$omp critical (c_count)
! obj not created on entry     count = count + 1
!$omp parallel private(obj,...) !$omp end critical
istat = create(obj, ...)   end function foo_create
: ! do computations
istat = destroy(obj)   function foo_destroy(this)
!$omp end parallel     :
! obj undefined !$omp critical (c_count)
    count = count ­ 1
!$omp end critical
beware definition status   end function foo_destroy
updates on count are not
imagine count public
thread-safe
inconsistencies/wrong valuues good luck finding problems

©2006-8 LRZ 36
Global variables (3)
migrating COMMON blocks

problems with COMMON


programming effort does not scale well
needs extensive documentation
keep track of all read/write accesses
replace by module globals
replace include by use
minimal effort: public attribute for all globals
retains disadvantage of access to all globals by all program units
improve by
encapsulating
adding accessor routines
need for large-scale access may require some redesign

©2006-8 LRZ 37
Recommendations:
The EQUIVALENCE statement

Used in Fortran 77 under three possible motivations:


1. reuse of storage
2. referencing
3. changing representation
Should not be used in modern codes
motive 1: Obsolete. Replace by dynamic memory management
motive 2: Replace by pointers (see later)
motive 3: If you're absolutely sure you need this, use the
transfer() intrinsic

transfer(source, mold[, size])

... a fully polymorphic one at that


source and mold can be of any type, scalar or array
function result is of type mold
distinct storage area
©2006-8 LRZ 38
An example for the use of transfer()

from type(xtf) integer function xtf_size(this)


  type(xtf), intent(in) :: this
to an integer array   integer :: mold(1)
type, public :: xtf   xtf_size = &
  integer :: n        size(transfer(this, mold))
  real(dk) :: d(20) end function xtf_size
  character(len=17) :: str : ! and here the usage
end type xtf type(xtf) :: x
: integer :: i
subroutine xtf_to_int(out,in) integer, allocatable :: istore(:)
  integer, intent(out) :: out(:) x = xtf(...) ! initialize
  type(xtf), intent(in) :: in i = xtf_size(x)
  integer :: mold allocate(istore(i))
  out(:) = transfer(in, mold, & call xtf_to_int(istore, x)
                    size(out)) :
end subroutine xtf_to_int
use special property of
transfer()
required size of out(:) is
returns array of size needed
unknown
©2006-8 LRZ 39
Recommendations:
Statement functions

Fortran 77 idiom Replace by


internal procedure
real(rk) :: parab, a, b, c still cannot use as function
parab(x) = a * x * x + b * x + c argument
:
a = ...; b = ...; c = ... program myprog
y1 = parab(x1)   implicit none
y2 = parab(x2)   real(rk) :: a, b, c
  a = ...; b = ...; c = ...
note access to host   y1 = parab(x1)
  y2 = parab(x2)
often inlined by compiler contains
for efficient execution   real(rk) function parab(x)
limitations:   :
cannot use as function argument end program

no explicit interface
module procedure
not as optimizable
©2006-8 LRZ 40
A case study:
Function arguments and threading (1)

Typical task: numerical integration


use a canned routine (NAG: D01AHF)
do multiple integrations  why not in parallel?
!$omp parallel do
do i=istart,iend
  ... ! prepare 
  call d01ahf(..., my_fun, ...)
end do
!$omp end parallel do
Pitfalls:
Is the vendor routine thread-safe?  documentation/tests
How are function calls (my_fun) treated?  discussed now

©2006-8 LRZ 41
A case study:
Function arguments and threading (2)

Discrepancy:
interface required by vendor

real(dk) function arg_fun(x)
  real(dk), intent(in) :: x
end function

interface provided by user

subroutine user_fun(x, n, p, r)
  real(dk), intent(in) :: x, p
  integer, intent(in) :: n
  real(dk), intent(out) :: r
end function

©2006-8 LRZ 42
A case study:
Function arguments and threading (3)

Wrap user_fun() with globals possible client usage


assume par depends on
module mod_user_fun integration result
  double precision, save :: par
  integer, save :: n
!$omp threadprivate (par, n)   par = ...
contains  !$omp parallel do copyin(par)
  function arg_fun(x) result(r)   do i=istart, iend
    double precision :: r, x     n = ...
    call d01ahf(..., &
    call user_fun(x, n, par, r)          arg_fun, ...)
  end function arg_fun     par = ...
  subroutine user_fun(...) ! depends on previous call
  :
  end do
  end subroutine user_fun
!$omp end parallel do
end module my_fun_module

©2006-8 LRZ 43
A case study:
Function arguments and threading (4)

Still rather cumbersome to use


„non-local programming“
thread-private versus shared and locked variable from
previous example
need to know exactly what to use where
account for:
values undefined for thread-privates without copyin
values not preserved across multiple parallel regions
note: serial regions OK, value of master thread
Question:
is there not a better way on the language level?
what about using generics?
not allowed as arguments for procedures

©2006-8 LRZ 44
Fortran pointers:
a contrast to C

C pointers Fortran pointers


reference an address reference data („target“)
can be associated with a rather like C++ references
type target requires correspondingly
but need not be named attribute
e.g., polymorphic void * strongly typed
can be used for in particular, statically typed in
dynamically allocated Fortran 95
storage
target can be created
iterative referencing dynamically
possible
may reference target
int **p  array (slice)
e.g., for construction of 2d arrays possibly with strides in each
dimension
„pointer to pointer“ is not
a Fortran concept
©2006-8 LRZ 45
Fortran pointers:
declaration and possible status

pointer declaration: associated(ptr[,target])


type (xx), pointer :: & intrinsic
  p_xx => null() logical function
allows to distinguish cases
1. and 2. above
real, dimension(:), &
undefined pointers:
  pointer :: p_r => null() no dereferencing possible
only usable intrinsic:
pointer status can be nullify()
=> null() initialization:
1. associated introduced in Fortran 95
2. disassociated enforce disassociation at
beginning of program
3. undefined

©2006-8 LRZ 46
Fortran pointers:
definition variants

There are three possibilities for assignment


pointer assignment
turns previously undefined pointer into associated pointer or
changes target of previously associated pointer
pointer allocation
associates pointer with dynamically allocated storage
otherwise similar to pointer assignment
target assignment
copying between targets
needs care, especially where dynamically sized entities are
concerned

©2006-8 LRZ 47
Fortran pointers:
pointer assignment and allocation

point at an existing target: generate target


type (xx), target :: t dynamically:
type (xx), pointer :: & previous example: pointer
to array
  p_xx, q_xx => null()
real, dimension(:), &
:
  pointer :: p_r => null()
p_xx => t
:
p_xx is associated with t
allocate(p_r(1000))
dereferences go to t -
p_r is associated with
Example pointer
allocated storage area
assignment:
storage area
q_xx => p_xx automatically has target
does the same (rhs attribute
dereference) as dereferences go to
q_xx => t storage area

©2006-8 LRZ 48
Notes (1): Some differences between
the pointer and allocatable attributes

Pointer usage Allocatable usage

subroutine do_stuff(...,r,...) subroutine do_stuff(...,r,...)
  real, target :: r   real, target :: r
  :   :
  real, pointer :: a_loc(:)   real, allocatable :: a_loc(:)
  real, pointer :: s_loc !   real, allocatable :: s_loc

  allocate(a_loc(...))   allocate(a_loc(...))
  s_loc => r !   s_loc = r
  :   :
  if (associated(a_loc)) then   if (allocated(a_loc)) then
    :     :
  end if   end if
  deallocate(a_loc) !   deallocate(a_loc)
end subroutine end subroutine

©2006-8 LRZ 49
Notes (2)
Pointers to arrays

may reference discontiguous what about array of pointers?


storage area: not as a separate Fortran
real, dimension(:), & concept
but can use derived type
  pointer :: p_r => null()
: type arr_xxptr
p_r => a(1:253:8)     type(xx), pointer :: xx
dereferences go to   end type
a(1),a(9),a(17),...
  :
if specified as actual
subroutine argument   type(xx), target :: x
compiler may need to use copy-   type(arr_xxptr), &
in/copy-out        dimension(100) :: xxp
otherwise no loop optimization   : 
possible inside subroutine   xxp(23)%xx => x
performance impact due to
copying
©2006-8 LRZ 50
Fortran pointers:
target assignment

„=“ assignment:
assume xx is a type with static components
could also be intrinsic type
type (xx), pointer :: p2
      type (xx), target :: tt
      :
Intel compiler:
      p_xx => tt ­check pointer option
      : should identify incorrect target
      allocate(p2) assignments at run time
      p2 = p_xx
target of p_xx is copied to target of p2
i.e. both left and right hand sides are dereferenced
both p_xx and p2 must be associated with a target
types with dynamic components: to be discussed later
©2006-8 LRZ 51
Fortran pointers:
handling subroutine arguments
Actual Dummy Argument
Argument object pointer target
usually by reference, pointer assoc. with
copy-in/copy-out dummy argument
object allowed, not allowed becomes undefined
no-alias assumption on return

must be associated, same rank,


dereference to target, assoc. status passed, pointer assoc. with
pointer may need copy-in/ beware invalid target dummy arg.
copy-out (efficiency) (upon return) is preserved.
copy-in/copy-out
not allowed for
usually by reference, scalar and assumed
copy-in/copy-out
target allowed, not allowed shape array dummies,
same shape required
no-alias assumption
explicit interface required
©2006-8 LRZ 52
Fortran pointers:
overview of potential problems

dangling references dereference undefined


if target is deallocated or pointers
goes out of scope behaviour is not specified
associated pointers become by standard
undefined (non-locally) not a standard conforming
memory leaks program
if all references to a anything may happen
dynamically allocated crash of program
target are disassociated error message from run time
from this target system
possibly due to pointer going out of performance issues for run time
scope checking

©2006-8 LRZ 53
Derived data types
with dynamical components

Motivations:
In practice, the size of object (array) components is often
unknown at compile time
Want to implement information structures like lists and trees
may need to reference type recursively (or in a multiply recursive manner) within a type
definition
(potential) Issues:
memory management: prevent memory leaks

type definition simple linked list type definition cluster of bodies

type :: cluster
type :: list_xx
  integer :: num_bodies = 0
  type(xx) :: xx
  real, allocatable :: &
  type(list_xx), pointer :: &
       mass(:) ! must be array
       next => null()
  :
end type
end type

©2006-8 LRZ 54
Types with pointer components:
Assignment rules extended

Suppose we have
type(list_xx) :: l1, l2
type(xx) :: o_xx

o_xx = ...
l1%xx = o_xx
allocate(l1%next)

then the following two alternatives are equivalent:


l2%xx = l1%xx
l2 = l1
l2%next => l1%next

in particular, no new storage is allocated


i.e., pointer components are pointer assigned
this is also called „shallow copy“

©2006-8 LRZ 55
Types with pointer components
Structure constructors

Need either a target or null() for pointer component


Example:

type(list_xx) :: l1
type(list_xx), target :: l2

l2 = list_xx(xx(...), null())

l1 = list_xx(xx(...), l2)

Further requirement: Either


access to type definition(s) by host association, or
public types with public components

©2006-8 LRZ 56
Types with allocatable components:
Assignment rules extended

Two equivalent code sections

type(cluster) :: c1, c2 type(cluster) :: c1, c2
: :
c1%num_bodies = 5
allocate(c1%mass(5)) c1 = cluster(5, (/ ... /))
c1%mass = (/ ... /)
: :
c2 = c1 c2 = c1

assignment automatically (re)allocates c2%mass(:) to the


right size
„deep copy“ is performed i. e., the content is physically
duplicated or cloned
Again, access to components must be allowed

©2006-8 LRZ 57
Further properties of
pointer vs. allocatable type components

Property pointer component allocatable component

enable sizing at run time yes yes

may reference type recursively yes no

default initialization null() only no

auto-allocation no yes

auto-deallocation when out of scope no yes

assignment performs shallow copy deep copy

object with parameter attribute null() only no (yes in Fortran 2003)

scalar component yes no (yes in Fortran 2003)

©2006-8 LRZ 58
Allocating dynamic entities
within subroutines
Given the situation
program myprog
  :
  type(foo), allocatable :: f(:,:) pointer possible,
  : but with performance issues
shape of allocatable typically generated within a subroutine
if values are to be read into array as well, a second subroutine
is needed. This is not very economical
allows following factory method:
subroutine alloc_foo_arr(f, unit)
  type(foo), allocatable, intent(out) :: f(:,:)
  integer, intent(in) :: unit
  integer :: n, m
  read(unit) :: n, m  ! checks omitted
  allocate(f(n, m), stat=...)
  read(unit) :: f
end subroutine

©2006-8 LRZ 59
Rules for interfaces
using allocatable dummy arguments

explicit interface required array itself:


actual argument must be copy-in/copy-out is
allocatable, and permitted unless both
actual and dummy
have same type, kind
argument have the
parameters and rank as
target attribute
dummy argument
specifying intent
allocation status:
intent(in) refers to both
descriptor is received by
dummy upon call descriptor and array
useless except as constraint on
copy-in/copy-out of
descriptor is allowed actual argument
no reference to actual argument intent(out): allocation
is allowed during execution of status becomes
procedure (e.g., module variable) deallocated upon starting
procedure execution

©2006-8 LRZ 60
Some properties of functions (1)

Function result:
Assigning function results
is an object
is not a dummy argument type(foo) function foo_xx(...)
even if behaviour in some respects   :
like an intent(out) argument end function foo_xx
:
exists temporarily only   type(foo) :: res
until assignment to lhs of function   res = foo_xx(...)
call completed
can be an object of any usually, standard
type assignment rules hold
can be an array beware validity of pointer
can be a pointer components
can be allocatable assignment may have
explicit interface been overloaded
needed for most of the lhs might even be different type
above than rhs
©2006-8 LRZ 61
Some properties of functions (2):
functions with pointer result

Size of result unknown Possible problems:


function do_stuff(...) result(r) undefined pointer if
  real, pointer :: r(:) target becomes undefined
  : automatic or allocatable targets -
! calculate size isz of result
  allocate(r(isz))
programming error
! fill r(:) with values subobjects of persistent objects –
end function do_stuff useful but require tracking
explicit interface needed memory leak if allocated
Client use: items are
not deallocated before leaving
real, pointer :: res_p(:)
scope
real :: res_a(ndim) target assigned instead of pointer
: assigned (example left)
res_p => do_stuff(...)
res_a = do_stuff(...) ! memory leak

©2006-8 LRZ 62
Some properties of functions (3):
functions with allocatable result

Fix some of the problems: Semantics:


function do_stuff(...) result(r) function result unallocated
  real, allocatable :: r(:) on entry
  : behaves as if it were dummy
! calculate size isz of result
argument with intent(out)
  allocate(r(isz))
! fill r(:) with values must be allocated and have
end function do_stuff defined value on return
explicit interface needed intermediate allocation /
deallocation is allowed
Client use:
after dereferencing: result
array is automatically
real, allocatable :: res_a(ndim) deallocated
: prevents memory leak
allocate(res_a(size(do_stuff(...)))
assignment of result still
! relies on absence of side effects
clunky in
res_a = do_stuff(...) 

©2006-8 LRZ 63

Potrebbero piacerti anche