Read derived types from namelist

I want to read a user-defined type from a namelist but I’m not sure whether is not possible or I’m doing something wrong. My approach:

  • The name list:
&type_nml
  a%x = 3.45d0
/
  • My code:
program read_type_nml
  implicit none
  type try
    real(8) :: x
  end type try
  type(try) :: a

  namelist /type_nml/ a%x
end program read_type_nml
  • My result:
$ gfortran read_type_nml.f90 
read_type_nml.f90:8:23:

   namelist /type_nml/ a%x
                       1
Error: Syntax error in NAMELIST statement at (1)

So I’m doing something wrong or is not possible to do this?

Thanks in advance!

I think the type component expression is not allowed in namelist.

Correct. Although in another context, that is used in the example

from an internal read, although there has been some argument about whether defining the namelist on a single line is acceptable or not (probably safer to use three, especially with IBM compiler).

@iarbina , @urbanjost :

If you don’t mind some code toward custom handling of NAMELIST components of derived type, you could consider defined input/output data transfers that the standard supports since Fortran 2003.

See a working example below if you wish to try (again, hope you don’t mind it’s with a commercial processor whose support staff I have bugged over the years to get this and other standard features working):

Click to see Fortran code

Defined input/output with NAMELIST

module point_m
   enum, bind(C)
      enumerator :: RED
      enumerator :: BLUE
      enumerator :: GREEN  
   end enum
   type :: point_t
      real :: x = 0.0
      real :: y = 0.0
      integer :: color = RED
   contains
      private
      procedure, pass(dtv) :: read_point_t
      procedure, pass(dtv) :: write_point_t
      generic, public :: read(formatted) => read_point_t
      generic, public :: write(formatted) => write_point_t
   end type

   character(len=*), parameter :: QUOTE = "'"
   character(len=*), parameter :: BEGIN_TOKEN = "<"
   character(len=*), parameter :: END_TOKEN = ">"

contains
 
   subroutine read_point_t(dtv, lun, iotype, vlist, istat, imsg)

      ! Argument list
      class(point_t), intent(inout)   :: dtv
      integer, intent(in)             :: lun
      character(len=*), intent(in)    :: iotype
      integer, intent(in)             :: vlist(:)
      integer, intent(out)            :: istat
      character(len=*), intent(inout) :: imsg

      ! Local variables
      real :: x
      real :: y
      integer :: color
      integer :: beg_idx, end_idx
      character(len=2048) :: nml_str
      namelist / point / x, y, color

      select case ( iotype )

         case ( "LISTDIRECTED" )
            ! Elided
         case ( "DT" )
            ! Elided
            if ( size(vlist) == 0 ) istat = 1
         case ( "NAMELIST" )
            read_blk: block
               read( unit=lun, fmt=*, iostat=istat, iomsg=imsg ) nml_str
               if ( istat /= 0 ) exit read_blk 
               beg_idx = index( nml_str, BEGIN_TOKEN )
               if ( beg_idx == 0 ) then
                  istat = 1
                  imsg = "Error parsing namelist string for point_t type"
                  exit read_blk
               end if 
               end_idx = index( nml_str, END_TOKEN, back=.true. )
               if ( end_idx == 0 ) then
                  istat = 2
                  imsg = "Error parsing namelist string for point_t type"
                  exit read_blk
               end if 
               if ( beg_idx + 1 >= end_idx-1 ) then
                  istat = 3
                  imsg = "Error parsing namelist string for point_t type"
                  exit read_blk
               end if 
               read( unit=nml_str(beg_idx+1:end_idx-1), nml=point, iostat=istat, iomsg=imsg )
               if ( istat /= 0 ) exit read_blk
               dtv%x = x
               dtv%y = y
               dtv%color = color 
            end block read_blk 
            return
         case default

      end select

      return

   end subroutine read_point_t

   subroutine write_point_t( dtv, lun, iotype, vlist, istat, imsg )

      ! Argument list
      class(point_t), intent(in)       :: dtv
      integer, intent(in)              :: lun
      character(len=*), intent(in)     :: iotype
      integer, intent(in)              :: vlist(:)
      integer, intent(out)             :: istat
      character (len=*), intent(inout) :: imsg

      ! Local variables
      real :: x
      real :: y
      integer :: color
      character(len=2048) :: nml_str
      namelist / point / x, y, color

      istat = 0
      select case ( iotype )
         case ( "LISTDIRECTED" )
            ! Elided
         case ( "DT" )
            ! Elided
            if ( size(vlist) == 0 ) istat = 1
         case ( "NAMELIST" )
            x = dtv%x
            y = dtv%y
            color = dtv%color
            write( unit=nml_str, nml=point, iostat=istat, iomsg=imsg )
            if ( istat == 0 ) then
               write( unit=lun, fmt="(*(g0))", iostat=istat, iomsg=imsg) QUOTE, BEGIN_TOKEN,        &
                  trim(nml_str), END_TOKEN, QUOTE
            end if 
            return
      end select

      return

   end subroutine

end module
   use, intrinsic :: iso_fortran_env, only : stdout => output_unit
   use point_m
   
   type(point_t) :: point
   character(len=:), allocatable :: stream_data
   character(len=:), allocatable :: msg
   character(len=2048) :: imsg
   integer :: istat
   namelist / dat / msg, point

   stream_data = "&dat msg='Hello World!', point=" // QUOTE // BEGIN_TOKEN // &
     "&point x=1.0, y=2.0, color=1 /" // END_TOKEN // QUOTE // "/"
   allocate( character(len=132) :: msg )
   read( stream_data, nml=dat, iostat=istat, iomsg=imsg )
   if ( istat == 0 ) then
      print *, "point%x = ", point%x, "; expected is 1.0" 
      print *, "point%y = ", point%y, "; expected is 2.0" 
      print *, "point%color = ", point%color, "; expected is 1"
      msg = trim(msg)
      write( stdout, nml=dat )
   else
      print *, "Namelist read failed: iostat = ", istat
      print *, trim(imsg)
   end if 
end     

C:\temp>ifort /standard-semantics p.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.3.0 Build 20210609_000000
Copyright (C) 1985-2021 Intel Corporation. All rights reserved.

Microsoft (R) Incremental Linker Version 14.29.30038.1
Copyright (C) Microsoft Corporation. All rights reserved.

-out:p.exe
-subsystem:console
p.obj

C:\temp>p.exe
point%x = 1.000000 ; expected is 1.0
point%y = 2.000000 ; expected is 2.0
point%color = 1 ; expected is 1
&DAT
MSG = Hello World!,
POINT= ‘<&POINT X= 1.000000 ,Y= 2.000000 ,COLOR= 1/>’
/

C:\temp>

Though already mentioned above, I think you can read a by specifying its name in the namelist (rather than the name of a component), something like…

!! test.f90
program main
  implicit none
  type try
    real(8) :: x = 100.0d0, y
    integer :: n = 555
  end type
  type(try) :: a

  namelist /type_nml/ a

  open( 10, file="test.dat", status="old" )  !! (or use "newunit=" keyword)
  read( 10, type_nml )   !! specify namelist here
  close( 10 )
  
  print type_nml
end

!! test.dat
&type_nml
  a%x = 1.23
  a%y = 4.56
/

$ gfortran-10 test.f90 && ./a.out

&TYPE_NML
 A%X=  1.2300000000000000     ,
 A%Y=  4.5599999999999996     ,
 A%N=555        ,

IIRC, only the components specified in the input file will be modified (e.g., a % n is not modified in the above case), so it can be used just to read only a% x, for example.

Btw, I really hope the output of namelist will be in lowercases… (hopefully with one space at both sides of the equal sign). But the output format is compiler-dependent (and also prints all of the contents of a type, including the full strings), so it may be more convenient to just write custom output routines. (In my case, I usually make “a % show()” like method).

For the program of @FortranFan, gfortran 12.0.0 20210718 from equation.com gives output

 Namelist read failed: iostat =           -1
 End of file

I appreciate the presentation of advanced features of Fortran, even if gfortran does not yet support them.

Interesting approach. Thanks!

Thanks a lot! This actually solved my problem/question!

I do this by using temporary variables–plain reals and integers etc, that are then assigned to the type parameters.