Search code examples
iofortranfortran2003derived-types

How to write an allocatable array in a derived type using namelists?


I am having trouble writing an allocatable array nested in a derived type using namelists. A minimal example is shown below. How can I modify the program to have the allocatable array inside the derived type work as though it were not nested?

program test

    implicit none

    type struct_foo
        integer, allocatable :: nested_bar(:)
    end type struct_foo

    integer, allocatable :: bar(:)
    type(struct_foo) :: foo
    ! namelist / list / foo, bar
    namelist / list / bar

    allocate(bar(5))
    bar = [1:5]

    allocate(foo%nested_bar(5))
    foo%nested_bar=[1:5]

    write(*,list)

end program test

With the foo commented out of the namelist, it works just fine, producing the output:

 &LIST
 BAR     =           1,           2,           3,           4,           5
 /

With foo included, the program fails to compile:

>> ifort -traceback test_1.f90 -o test && ./test
test_1.f90(20): error #5498: Allocatable or pointer derived-type fields require a user-defined I/O procedure.
    write(*,list)
--------^
compilation aborted for test_1.f90 (code 1)

Solution

  • As the error message states, you need to provide a user defined derived type I/O (UDDTIO) procedure. This is required for input/output of any object with an allocatable or pointer component.

    How the object of derived type is formatted in the file is completely under the control of the UDDTIO procedure.

    An example, using a very simple output format, is below. Typically a UDDTIO procedure implementing namelist output would use an output format that was consistent with the other aspects of namelist output and typically there would also be a corresponding UDDTIO procedure that was then able to read the formatted results back in.

    module foo_mod
      implicit none
    
      type struct_foo
        integer, allocatable :: nested_bar(:)
      contains
        procedure, private :: write_formatted
        generic :: write(formatted) => write_formatted
      end type struct_foo
    contains
      subroutine write_formatted(dtv, unit, iotype, v_list, iostat, iomsg)
        class(struct_foo), intent(in) :: dtv
        integer, intent(in) :: unit
        character(*), intent(in) :: iotype
        integer, intent(in) :: v_list(:)
        integer, intent(out) :: iostat
        character(*), intent(inout) :: iomsg
    
        integer :: i
    
        if (allocated(dtv%nested_bar)) then
          write (unit, "(l1,i10,i10)", iostat=iostat, iomsg=iomsg)   &
              .true.,  &
              lbound(dtv%nested_bar, 1),  &
              ubound(dtv%nested_bar, 1)
          if (iostat /= 0) return
          do i = 1, size(dtv%nested_bar)
            write (unit, "(i10)", iostat=iostat, iomsg=iomsg)  &
                dtv%nested_bar(i)
            if (iostat /= 0) return
          end do
          write (unit, "(/)", iostat=iostat, iomsg=iomsg)
        else
          write (unit, "(l1,/)", iostat=iostat, iomsg=iomsg) .false.
        end if
      end subroutine write_formatted
    end module foo_mod
    
    program test
      use foo_mod
    
      implicit none
    
      integer, allocatable :: bar(:)
      type(struct_foo) :: foo
      namelist / list / foo, bar
    
      allocate(bar(5))
      bar = [1:5]
    
      allocate(foo%nested_bar(5))
      foo%nested_bar=[1:5]
    
      write (*,list)
    end program test
    

    Use of UDDTIO obviously requires a compiler that implements this Fortran 2003 language feature.