Clicky

Fortran Wiki
Object-oriented programming

Pseudo Object Oriented Style in Fortran 90

Derived types

In object-oriented languages such as C++, one can define classes that contain both data and methods which operate on that data. One can then create separate instances of the class, each with its own data. A method called from an instance of the class will work on the data held by that particular instance.

In Fortran, modules may contain data, but there is no notion of separate instances of a module. In order to obtain class-like behavior, one can combine a module, which contains the methods that operate on the class, with a derived type containing the data. There can be separate “instances” of the type in that we can allocate many variables of that type which can be passed as parameters to the methods contained in the module.

To illustrate these points consider a simple Circle class, based on an example in Akin (2003, p. 34):

module class_Circle
  implicit none
  private
  public :: Circle, circle_area, circle_print

  real :: pi = 3.1415926535897931d0 ! Class-wide private constant

  type Circle
     real :: radius
  end type Circle
contains
  function circle_area(this) result(area)
    type(Circle), intent(in) :: this
    real :: area
    area = pi * this%radius**2
  end function circle_area

  subroutine circle_print(this)
    type(Circle), intent(in) :: this
    real :: area
    area = circle_area(this)  ! Call the circle_area function
    print *, 'Circle: r = ', this%radius, ' area = ', area
  end subroutine circle_print
end module class_Circle

program circle_test
  use class_Circle
  implicit none

  type(Circle) :: c     ! Declare a variable of type Circle.
  c = Circle(1.5)       ! Use the implicit constructor, radius = 1.5.
  call circle_print(c)  ! Call a class subroutine
end program circle_test

NOTE: circle_print and circle_area could be made accessible through generic procedure names print and area, respectively. That gets closer to a standard OOP language, because print can be defined for multiple “sub-classed” objects.

Run-Time Polymorphism


Object-Oriented Features of Fortran 2003

Type-bound procedures

New features in Fortran 2003 allow us to improve upon the object-oriented approach above by using type-bound procedures which allow us to write

a = c%area
call c%print

instead of the more verbose

a = circle_area(c)
call circle_print(c)

If the above example is modified to provide print and area as generic interfaces, it is more concise, even though it does not follow modern OOP syntax:

a = area(c)
call print(c)

The program above, modified to use type-bound procedures, looks like this

module class_Circle
  implicit none
  private
  real :: pi = 3.1415926535897931d0 ! Class-wide private constant

  type, public :: Circle
     real :: radius
   contains
     procedure :: area => circle_area
     procedure :: print => circle_print
  end type Circle
contains
  function circle_area(this) result(area)
    class(Circle), intent(in) :: this
    real :: area
    area = pi * this%radius**2
  end function circle_area

  subroutine circle_print(this)
    class(Circle), intent(in) :: this
    real :: area
    area = this%area()  ! Call the type-bound function
    print *, 'Circle: r = ', this%radius, ' area = ', area
  end subroutine circle_print
end module class_Circle


program circle_test
  use class_Circle
  implicit none

  type(Circle) :: c     ! Declare a variable of type Circle.
  c = Circle(1.5)       ! Use the implicit constructor, radius = 1.5.
  call c%print          ! Call the type-bound subroutine
end program circle_test

Note that we have changed the dummy parameters from type(Circle) to class(Circle). We can then call the class functions in an object-oriented fashion as a = c%area and call c%print, where the type is passed to the first argument of the functions circle_area and circle_print automatically. See Metcalf, Reid, and Cohen (2004, p. 279) for additional information.


See Also

References