Chiel
Chiel

Reputation: 6194

Assign pointer in derived type to target in the same type in Fortran

I would like to assign a pointer in a derived type that is contained in the same derived type. The code below gives me the error below. What is going on here, and how can I solve this?

   24 |         zoos(i)%tigers(1) => zoos(i)%animals(1, 1)
      |        1
Error: Expected bounds specification for 'zoos' at (1)
module mo_zoo
    implicit none
    type zoo
        integer, dimension(:,:), pointer :: animals
        integer, dimension(:), pointer :: tigers
        integer, dimension(:), pointer :: ducks
    end type zoo
   
    save
        type(zoo), dimension(:), pointer :: zoos
end module mo_zoo

program test
    use mo_zoo
    implicit none
    integer :: n_zoos
    integer :: i

    n_zoos = 4
    allocate(zoos(n_zoos))

    do i = 1, n_zoos
        allocate(zoos(i)%animals(10, 2))
        zoos(i)%tigers(1) => zoos(i)%animals(1, 1)
        zoos(i)%ducks(1) => zoos(i)%animals(1, 2)
    end do
end program test

Upvotes: 3

Views: 502

Answers (1)

veryreverie
veryreverie

Reputation: 2981

The problem is nothing to do with the derived type, and the error message is wrong.

The problem is that zoos(i)%ducks is a pointer to an array, not an array of pointers, so you need to point zoos(i)%ducks at zoos(i)%animals(:, 2), rather than zoos(i)%ducks(1) at zoos(i)%animals(1, 2).

I have previously talked about this in this answer.

I believe this does what you want:

module mo_zoo
    implicit none
    type zoo
        integer, dimension(:,:), pointer :: animals
        integer, dimension(:), pointer :: tigers
        integer, dimension(:), pointer :: ducks
    end type zoo
   
    save
        type(zoo), dimension(:), pointer :: zoos
end module mo_zoo

program test
    use mo_zoo
    implicit none
    integer :: n_zoos
    integer :: i

    n_zoos = 4
    allocate(zoos(n_zoos))

    do i = 1, n_zoos
        allocate(zoos(i)%animals(10, 2))
        zoos(i)%tigers => zoos(i)%animals(:, 1)
        zoos(i)%ducks => zoos(i)%animals(:, 2)
    end do
end program test

I would also like to offer a frame challenge. As noted in the comments, particularly by Ian Bush, pointers in Fortran are notoriously error-prone.

I would recommend replacing any allocated pointers with allocatables. These allocatables also need to be targets if they have pointers pointing at them, like so:

module mo_zoo
    implicit none
    type zoo
        integer, dimension(:,:), allocatable :: animals
        integer, dimension(:), pointer :: tigers
        integer, dimension(:), pointer :: ducks
    end type zoo
   
    save
        type(zoo), dimension(:), allocatable, target :: zoos
end module mo_zoo

program test
    use mo_zoo
    implicit none
    integer :: n_zoos
    integer :: i

    n_zoos = 4
    allocate(zoos(n_zoos))

    do i = 1, n_zoos
        allocate(zoos(i)%animals(10, 2))
        zoos(i)%tigers => zoos(i)%animals(:, 1)
        zoos(i)%ducks => zoos(i)%animals(:, 2)
    end do
end program test

There are a number of advantages to using allocatables over pointers where possible:

  • The memory held by allocatables will automatically be freed when the allocatables drop out of scope1.
  • Compilers can make stronger assumptions about allocatables than about pointers, sometimes leading to faster code.

1 At least, this is true according to the Fortran standard. There are several outstanding problems with some compilers (notably this bug) relating to finalisation.

Upvotes: 5

Related Questions