Search code examples
pythonoopfortranderived-types

How to implement a type-bound write statement output in a Fortran derived-type or class


Suppose I have this simple class:

   Module Foo
        ...
        character(len=3), parameter :: describe_Foo=(/'BAR', 'BED', 'BOD'/)
        ...
        type :: A
            real :: value
            integer :: descriptor
        contains
            procedure :: getter
            procedure :: setter
            ...
        end type A

   contains
        function writetype(self,...)
            ...
            write(writetype,*) self%value, describe_foo(self%descriptor)
        end function writetype
   ...
   end module Foo

How can I define its interface to "write" so that every time this type is passed to the write statement it outputs the string defined by the class method writetype.

In other words, in Python parlance, can I implement the equivalent of an __str__() method?

I've found tantalizing tidbits suggesting that this is possible, see User-defined derived-type Input/Output procedures (Fortran 2003) and User-defined derived-type Input/Output procedure interfaces (Fortran 2003). These documents give ample information to write the methods I need, but it is still unclear to me how to define an interface or procedure specification so that the behavior I want occurs.

Example application:

program test
    ...
    type(A) :: bartype, bedtype
    ...
    bartype=A(120.0,1)
    bedtype=A(102.0,2)
    write(*,*) bartype,bedtype
end program test

Desired output:

>test.exe
 120.0000 BAR
 102.0000 BED

Solution

  • You need to have a generic WRITE(FORMATTED) binding, bound to a specific procedure that has suitable characteristics. See section 9.6.4.8 in the F2008 standard for more information.

    type :: A
      real :: value
      integer :: descriptor
    contains
      procedure :: writetype
      generic :: write(formatted) => writetype
    end type A
    ...
    subroutine writetype(dtv, unit, iotype, v_list, iostat, iomsg)
      ! Argument names here from the std, but you can name them differently.
      class(A), intent(in) :: dtv         ! Object to write.
      integer, intent(in) :: unit         ! Internal unit to write to.
      character(*), intent(in) :: iotype  ! LISTDIRECTED or DTxxx
      integer, intent(in) :: v_list(:)    ! parameters from fmt spec.
      integer, intent(out) :: iostat      ! non zero on error, etc.
      character(*), intent(inout) :: iomsg  ! define if iostat non zero.
      ...
      write (unit, "(F9.4,1X,A)", IOSTAT=iostat, IOMSG=iomsg)  &
          dtv%value, describe_foo(dtv%descriptor)
    end subroutine writetype
    

    It is probably also worth noting that you need a compiler that implements this!