Fortran Wiki

Below is a generic linked list example which uses the transfer intrinsic to store arbitrary data (or pointers to arbitrary data types) in list nodes. This code is based on the following paper:

Blevins, J. R. (2009). A Generic Linked List Implementation in Fortran 95. ACM SIGPLAN Fortran Forum 28(3), 2-7.


First, we have the list module which defines the linked list object (or rather, an individual node object) and associated procedures. The node data element is defined as a pointer to an array of type integer. The intention is not that the list will store arrays of integers, but rather that the user can make use of the transfer intrinsic to store arbitrary data, or even pointers to arbitrary data, in the list. This will be illustrated below.

! A generic linked list object
module list
  implicit none


  public :: list_t
  public :: list_data
  public :: list_init
  public :: list_free
  public :: list_insert
  public :: list_put
  public :: list_get
  public :: list_next

  ! A public variable to use as a MOLD for transfer()
  integer, dimension(:), allocatable :: list_data

  ! Linked list node data type
  type :: list_t
     integer, dimension(:), pointer :: data => null()
     type(list_t), pointer :: next => null()
  end type list_t


  ! Initialize a head node SELF and optionally store the provided DATA.
  subroutine list_init(self, data)
    type(list_t), pointer :: self
    integer, dimension(:), intent(in), optional :: data


    if (present(data)) then
       self%data = data
    end if
  end subroutine list_init

  ! Free the entire list and all data, beginning at SELF
  subroutine list_free(self)
    type(list_t), pointer :: self
    type(list_t), pointer :: current
    type(list_t), pointer :: next

    current => self
    do while (associated(current))
       next => current%next
       if (associated(current%data)) then
       end if
       current => next
    end do
  end subroutine list_free

  ! Return the next node after SELF
  function list_next(self) result(next)
    type(list_t), pointer :: self
    type(list_t), pointer :: next
    next => self%next
  end function list_next

  ! Insert a list node after SELF containing DATA (optional)
  subroutine list_insert(self, data)
    type(list_t), pointer :: self
    integer, dimension(:), intent(in), optional :: data
    type(list_t), pointer :: next


    if (present(data)) then
       next%data = data
    end if

    next%next => self%next
    self%next => next
  end subroutine list_insert

  ! Store the encoded DATA in list node SELF
  subroutine list_put(self, data)
    type(list_t), pointer :: self
    integer, dimension(:), intent(in) :: data

    if (associated(self%data)) then
    end if
    self%data = data
  end subroutine list_put

  ! Return the DATA stored in the node SELF
  function list_get(self) result(data)
    type(list_t), pointer :: self
    integer, dimension(:), pointer :: data
    data => self%data
  end function list_get

end module list

The data module defines a generic data type data_t which will illustrate how to store pointers to user-defined types in the list. data_t will contain whatever elements are necessary to store your particular data. We also define a data_ptr type which contains only a pointer to an object of type data_t. This is a trick which allows us to store pointers in the list, rather than copying entire data_t structures which may potentially be very large.

! A derived type for storing data.
module data
  implicit none

  public :: data_t
  public :: data_ptr

  ! Data is stored in data_t
  type :: data_t
     real :: x
  end type data_t

  ! A trick to allow us to store pointers in the list
  type :: data_ptr
     type(data_t), pointer :: p
  end type data_ptr
end module data

Finally, we have a simple control program which illustrates how to initialize and free the list and how to store and retrieve pointers to data_t objects using transfer.

! A simple generic linked list test program
program list_test
  use list
  use data
  implicit none

  type(list_t), pointer :: ll => null()
  type(data_t), target :: dat_a
  type(data_t), target :: dat_b
  type(data_ptr) :: ptr

  ! Initialize two data objects
  dat_a%x = 17.5
  dat_b%x = 3.0

  ! Initialize the list with dat_a
  ptr%p => dat_a
  call list_init(ll, DATA=transfer(ptr, list_data))
  print *, 'Initializing list with data:', ptr%p

  ! Insert dat_b into the list
  ptr%p => dat_b
  call list_insert(ll, DATA=transfer(ptr, list_data))
  print *, 'Inserting node with data:', ptr%p

  ! Get the head node
  ptr = transfer(list_get(ll), ptr)
  print *, 'Head node data:', ptr%p

  ! Get the next node
  ptr = transfer(list_get(list_next(ll)), ptr)
  print *, 'Second node data:', ptr%p

  ! Free the list
  call list_free(ll)
end program list_test


% gfortran -o list -std=f95 -Wall list.f90
% ./list
 Initializing list with data:   17.500000
 Inserting node with data:   3.0000000
 Head node data:   17.500000
 Second node data:   3.0000000


Jason Blevins (14 May 2009) Comments, observations, and improvements are welcome!


category: code