4

I'm trying to make a type, which would serve as a wrapper for an arbitrary other types, so I could make a heterogeneous array, as it's advised in Heterogeneous array of Fortran classes and Creating heterogeneous arrays in Fortran.

So, I've tried to implement it like this:

module m
implicit none

type :: container
    class(*), pointer, public :: item
end type container
end module m

program mwe 
use m

implicit none

type(container) :: cont
integer, target :: i

i = 5 
cont = container(i)

write(*,*) cont%item

end program mwe 

Now I'm getting error

test4.f90(20): error #5513: A polymorphic I/O list item requires a user-defined derived-type input/output procedure.
    write(*,*) cont%item
----^
compilation aborted for test4.f90 (code 1)

So I tried to implement I/O like this:

module m
    implicit none

    type :: container
        class(*), pointer, public :: item
    contains
        procedure :: write_sample => write_container_sample_impl
        procedure :: read_sample  => read_container_sample_impl

        generic   :: write(unformatted) => write_sample
        generic   :: read(unformatted) => read_sample
    end type container

contains

    subroutine write_container_sample_impl(this, unit, iostat, iomsg)
        class(container), intent(in)    :: this
        integer, intent(in)         :: unit
        integer, intent(out)        :: iostat
        character(*), intent(inout) :: iomsg

        write(unit, iostat=iostat, iomsg=iomsg) this%item
    end subroutine write_container_sample_impl

    subroutine read_container_sample_impl(this, unit, iostat, iomsg)
        class(container), intent(inout) :: this
        integer, intent(in)         :: unit
        integer, intent(out)        :: iostat
        character(*), intent(inout) :: iomsg

        read(unit, iostat=iostat, iomsg=iomsg) this%item
    end subroutine read_container_sample_impl

end module m

program mwe 
    use m

    implicit none

    type(container) :: cont
    integer, target :: i

    i = 5 
    cont = container(i)

    write(*,*) cont%item

end program mwe 

But the same error occurs even in my new methods:

test4.f90(22): error #5513: A polymorphic I/O list item requires a user-defined derived-type input/output procedure.
        write(unit, iostat=iostat, iomsg=iomsg) this%item
--------^
test4.f90(31): error #5513: A polymorphic I/O list item requires a user-defined derived-type input/output procedure.
        read(unit, iostat=iostat, iomsg=iomsg) this%item
--------^
test4.f90(47): error #5513: A polymorphic I/O list item requires a user-defined derived-type input/output procedure.
    write(*,*) cont%item
----^

So, I have two questions:

  1. How should I implement it correctly?
  2. Is it better/easier to declare item variable as a pointer or as an allocatable variable?
Eenoku
  • 2,741
  • 4
  • 32
  • 64
  • 1
    As to your 2nd question: my opinion is that we should always use allocatable variables rather than pointers where the specific capabilities of pointers (such as multiple pointers to the same target or to parts of the same target) are unnecessary. For one, the language guarantees that allocated entities will be deallocated automatically when they go out of scope so it is harder to program memory leaks. For two, personally I find them easier to work with than pointers and targets. – High Performance Mark Jun 06 '18 at 10:20

2 Answers2

2

Working with unlimited polymorphic entities is demanding.

Adding the defined input/output procedures for the container type doesn't solve your problem because the problem isn't with the container itself. Instead it is the component of the container which is polymorphic and requiring the defined I/O procedure.

Unfortunately though, because that component is unlimited polymorphic it isn't possible to define such a procedure.1

Further, your defined I/O procedures for the container type won't actually be used. You have defined procedures only for unformatted input and output, but write(*,*) is (list-directed) formatted output.

As to how you can solve this problem: at some point you have to decide what your unlimited polymorphic entity is. Part of the reason working with unlimited polymorphic entities is tricky is because intrinsic and derived types cannot be treated the same. As with your previous question, if you can use class(something_not_star) instead of class(*) you will find life easier.

With things how they are, select type is probably your best hope.


1 Defined I/O procedures may exist only for derived types.

francescalus
  • 30,576
  • 16
  • 61
  • 96
1

The way I implemented something similar was like this

module container_mod

implicit none
private
!Public access vars
public :: container

type container !< Unlimited polymorphic container class 
    private !contents are only accessible trough the methods, no direct access is allowed
    class(*), pointer :: value => null() !< value stored in container
contains
procedure :: getContent     !< returns stored content (pointer)
procedure :: storeContent   !< stores the provided values (sourced allocation)
procedure :: printContainer !< prints container contents (only primitive types implemented)
end type container

interface container
    procedure constructor !< construct/initialize a container
end interface

The procedures were defined like this

function getContent(this)
class(container), intent(in) :: this
class(*), pointer :: getContent
getContent => this%value
end function getContent

subroutine storeContent(this,to_store)
class(container), intent(inout) :: this
class(*), intent(in) :: to_store
allocate(this%value, source=to_store)
end subroutine storeContent

subroutine printContainer(this)
class(container), intent(in) :: this
select type(v => this%value)
type is (integer)
    print *, v
type is (character(*))
    print *, v(1:1)
type is (real)
    print *, v
    class default
    print*, "[printContainer]: don't know how to print this value, ignoring"
end select
end subroutine printContainer

function constructor(to_store)
class(container), pointer :: constructor
class(*), intent(in) :: to_store
allocate(constructor)
allocate(constructor%value, source=to_store)
end function constructor

Check out this repository as it implements a container class and uses it in an abstract heterogeneous array.

Once you retrieve the contents of the array tough, there is no way around typeguards I'm afraid, that is Fortran for you. If you do find a way, please let me know.

RBC
  • 11
  • 2
  • Hi, welcome to stack overflow. You should check the section of the help center about [promotion](https://stackoverflow.com/help/promotion), answers consisting of a link and little more are not the best option, as the link may be unreachable in the future, rendering the answer useless – OriolAbril Jun 06 '18 at 22:02