Fortran - How to write user-defined I/O for an array of different classes with the same parent? - class

I need to implement a function, which will serialize (i.e. save to an unformatted binary file) a class containing an array of objects, which belong to the same abstract class, but which belong to several different inherited classes.
The point is, that this array is passed to my function and it's created by user's actions. So, I have no way of knowing about the specific types being stored in the array.
Is there any way to implement write and read I/O methods in a way, which would automatically write the array without having to specify types of its single elements?
I have written this code to illustrate my situation:
module m
implicit none
type :: container
class(a), allocatable :: item
end type container
type, abstract :: a
integer, public :: num
contains
procedure :: write_impl => write_a
procedure :: read_impl => read_a
generic :: write(unformatted) => write_impl
generic :: read(unformatted) => read_impl
end type a
type, extends(a) :: b
integer, public :: num2
contains
procedure :: write_impl => write_b
procedure :: read_impl => read_b
end type b
type, extends(a) :: c
end type c
contains
subroutine write_a(this, unit, iostat, iomsg)
class(a), intent(in) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, iostat=iostat, iomsg=iomsg) this%num
end subroutine write_a
subroutine read_a(this, unit, iostat, iomsg)
class(a), intent(inout) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
read(unit, iostat=iostat, iomsg=iomsg) this%num
end subroutine read_a
subroutine write_b(this, unit, iostat, iomsg)
class(b), intent(in) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, iostat=iostat, iomsg=iomsg) this%num, this%num2
end subroutine write_b
subroutine read_b(this, unit, iostat, iomsg)
class(b), intent(inout) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
read(unit, iostat=iostat, iomsg=iomsg) this%num, this%num2
end subroutine read_b
end module m
program mwe
use m
implicit none
class(a), allocatable :: o1, o2, o3
class(container), allocatable :: arr(:)
integer :: i
o1 = b(1,2)
o2 = c(3)
allocate(arr(2))
arr(1) = container(o1)
arr(2) = container(o2)
! How to serialize 'arr' without specifying its elements' types?
end program mwe
So, is there any way, how to serialize such an array without having to manually specify, that o1 is of type b and o2 is of type c?
I need to be able to serialize an array of the abstract type a in general and also read it from the binary file without previous knowledge about its elements.

Related

How to override user-defined I/O procedures?

I have an abstract class with my read/write methods for unformatted binary streams. I also have some classes inherited from the abstract one and some of them have additional components I'd also like to serialize.
So, I want the first set of methods to serve as a "default" behavior and the methods overriding it inside the inherited classes to be used just by those specific classes.
I've tried to implement it like this:
module m
implicit none
type, abstract :: a
integer, public :: num
contains
procedure :: write_a
procedure :: read_a
generic :: write(unformatted) => write_a
generic :: read(unformatted) => read_a
end type a
type, extends(a) :: b
integer, public :: num2
contains
procedure :: write_b
procedure :: read_b
generic :: write(unformatted) => write_b
generic :: read(unformatted) => read_b
end type b
type, extends(a) :: c
end type c
contains
subroutine write_a(this, unit, iostat, iomsg)
class(a), intent(in) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, iostat=iostat, iomsg=iomsg) this%num
end subroutine write_a
subroutine read_a(this, unit, iostat, iomsg)
class(a), intent(inout) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
read(unit, iostat=iostat, iomsg=iomsg) this%num
end subroutine read_a
subroutine write_b(this, unit, iostat, iomsg)
class(b), intent(in) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, iostat=iostat, iomsg=iomsg) this%num, this%num2
end subroutine write_b
subroutine read_b(this, unit, iostat, iomsg)
class(b), intent(inout) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
read(unit, iostat=iostat, iomsg=iomsg) this%num, this%num2
end subroutine read_b
end module m
program mwe
use m
implicit none
class(a), allocatable :: o1, o2, o3
o1 = b(1,2)
o2 = c(3)
open(123, file='test5.dat', form='unformatted')
write(123) o1
close(123)
allocate(b :: o3)
open(123, file='test5.dat', form='unformatted')
read(123) o3
close(123)
write(*,*) o3%num, o3%num2
end program mwe
But I'm getting following error:
test5.f90(29): error #8638: The type/rank signature for arguments of this specific subroutine is identical to another specific subroutine that shares the same defined I/O. [WRITE_A]
subroutine write_a(this, unit, iostat, iomsg)
---------------^
test5.f90(86): error #6460: This is not a field name that is defined in the encompassing structure. [NUM2]
write(*,*) o3%num, o3%num2
--------------------------^
To me it seems, like the write method in the class a can't be overriden. How can I implement it properly?
This isn't a problem entirely to do with defined input/output procedures, but generic bindings and type-bound procedures more widely.
Your type b has the type-bound procedures as though (through the extension) it were defined like
type b
integer, public :: num, num2
contains
procedure :: write_a, write_b
procedure :: read_a, read_b
generic :: write(unformatted) => write_a, write_b
generic :: read(unformatted) => read_a, read_n
end type
The bindings write_a and write_b are indeed ambiguous (as are read_a and read_b). [More detail on that elsewhere.]
You don't really need those write_a and read_a bindings, so they should instead be overridden:
type, abstract :: a
integer, public :: num
contains
procedure :: write => write_a
procedure :: read => read_a
generic :: write(unformatted) => write
generic :: read(unformatted) => read
end type a
type, extends(a) :: b
integer, public :: num2
contains
procedure :: write => write_b
procedure :: read => read_b
end type b
Here the write and read bindings of type b override those of type a. The generic binding of write(unformatted) and read(unformatted) retain the mapping to the (now overridden) bindings in b.

Fortran 2008 - User-Defined I/O of class containing pointers

I have a following class
type :: net_t
private
character(:), allocatable :: net_type !< Type of the net
integer(kind=integer_4neuro) :: num_of_neurons !< Number of neurons in the net
character(:), allocatable :: training_method !< Used training method
class(neuron_t), allocatable :: neuron_arr(:) !< Array containing all neurons
integer(kind=integer_4neuro), allocatable :: input_neuron_arr(:) !< Array of input neuron indices
integer(kind=integer_4neuro), allocatable :: output_neuron_arr(:) !< Array of output neuron indices
class(connection_t), allocatable :: connection_arr(:) !< Array of all connections
contains
!> Prints information about the network to the standard output.
procedure :: print_info => print_info_impl
!> Saves the network instance to the Fortran binary file
procedure :: save_net_bin => save_net_bin_impl
!> Implementation of write function enabling the storage of
!! allocatable arrays.
procedure :: write_sample => write_sample_impl
!> Implementation of read function enabling to read
!! the whole instance of net_t stored as binary file.
procedure :: read_sample => read_sample_impl
generic :: write(unformatted) => write_sample
generic :: read(unformatted) => read_sample
end type net_t
I need to implement my own write and read functions to be able to serialize instances of net_t because it contains allocatable arrays.
I tried to implement the write function like this:
subroutine write_sample_impl(this, unit, iostat, iomsg)
class(net_t), intent(in) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
integer :: i !< Counter
! Write a record giving sizes for the allocation
write(unit, iostat=iostat, iomsg=iomsg) SIZE(this%neuron_arr), &
SIZE(this%input_neuron_arr), &
SIZE(this%output_neuron_arr), &
SIZE(this%connection_arr)
write(unit, iostat=iostat, iomsg=iomsg) (this%neuron_arr(i)%get_id(), &
this%neuron_arr(i)%get_potential(), &
this%neuron_arr(i)%get_state(), &
i=1,SIZE(this%neuron_arr)), &
this%input_neuron_arr, &
this%output_neuron_arr, &
(this%connection_arr(i)%get_input_neuron(), &
this%connection_arr(i)%get_output_neuron(), &
this%connection_arr(i)%get_weight(), &
i=1,SIZE(this%connection_arr))
end subroutine write_sample_impl
But now, another problem arose - I'm getting following error:
(this%connection_arr(i)%get_input_neuron(), &
1
Error: Data transfer element at (1) cannot be polymorphic unless it is processed by a defined input/output procedure
this%connection_arr(i)%get_output_neuron(), &
1
Error: Data transfer element at (1) cannot be polymorphic unless it is processed by a defined input/output procedure
I see the problem is, that the class connection_t contains pointers to neurons, as we can see here:
!> Represents a connection between two neurons.
type, extends(abstract_base_t) :: connection_t
private
class(neuron_t), pointer :: input_neuron !< Pointer to an input neuron
class(neuron_t), pointer :: output_neuron !< Pointer to an output neuron
real(kind=real_4neuro) :: weight !< Weight of the connection
contains
! Implementation of methods
end type connection_t
Is it possible to serialize pointers in this way? I wanted to use neurons to prevent copying of neuron_t instances themselves.

Error reading from Fortran binary file [duplicate]

I'm trying to write a simple code, which takes some objects with the same parental abstract class, stores them into a binary file and reads them back.
My code looks like this:
module m
implicit none
type :: container
class(a), allocatable :: item
end type container
type, abstract :: a
character(20), public :: obj_type
integer, public :: num
contains
procedure :: write_impl => write_a
procedure :: read_impl => read_a
generic :: write(unformatted) => write_impl
generic :: read(unformatted) => read_impl
end type a
type, extends(a) :: b
integer, public :: num2
contains
procedure :: write_impl => write_b
procedure :: read_impl => read_b
end type b
type, extends(a) :: c
end type c
contains
subroutine write_a(this, unit, iostat, iomsg)
class(a), intent(in) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, iostat=iostat, iomsg=iomsg) this%num
end subroutine write_a
subroutine read_a(this, unit, iostat, iomsg)
class(a), intent(inout) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
read(unit, iostat=iostat, iomsg=iomsg) this%num
end subroutine read_a
subroutine write_b(this, unit, iostat, iomsg)
class(b), intent(in) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, iostat=iostat, iomsg=iomsg) this%num, this%num2
end subroutine write_b
subroutine read_b(this, unit, iostat, iomsg)
class(b), intent(inout) :: this
integer, intent(in) :: unit
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
read(unit, iostat=iostat, iomsg=iomsg) this%num, this%num2
end subroutine read_b
end module m
program mwe
use m
implicit none
class(a), allocatable :: o1, o2, o3, o4
class(container), allocatable :: arr(:)
integer :: i, arr_size, tmp
character(20) :: str_tmp
o1 = b('b', 1, 2)
o2 = c('c', 3)
allocate(arr(2))
arr(1)%item = o1
arr(2)%item = o2
select type(t => o1)
type is(b)
write(*,*) t%num, t%num2
end select
select type(t => arr(1)%item)
type is(b)
write(*,*) t%num, t%num2
end select
write(*,*) 'Write into binary'
! WRITE size
open(123, file='test5.dat', form='unformatted')
write(123) SIZE(arr)
do i=1,2
write(123) arr(i)%item%obj_type
if(arr(i)%item%obj_type .eq. 'b') then
select type(t => arr(i)%item)
type is(b)
write(123) t
end select
else if(arr(i)%item%obj_type .eq. 'c') then
select type(t => arr(i)%item)
type is(c)
write(123) t
end select
end if
end do
close(123)
write(*,*) 'Read from binary'
open(123, file='test5.dat', form='unformatted')
read(123) arr_size
write(*,*) 'array size: ', arr_size
do i=1,2
read(123) str_tmp
write(*,*) str_tmp
if(allocated(o3)) deallocate(o3)
if(str_tmp .eq. 'b') then
allocate(b :: o3)
select type(t => o3)
type is(b)
read(123) t
write(*,*) t%num, t%num2 ! BAD OUTPUT
end select
else if(str_tmp .eq. 'c') then
allocate(c :: o3)
select type(t => o3)
type is(c)
read(123) t
write(*,*) t%num
end select
end if
end do
end program mwe
The problem is, with reading of o1 - it's of the type b, i.e. this object has two components - num and num2. I store it and I would naturally expect the read values to be the same, as the written ones.
But I'm getting the same strange behavior as described in Variables being deleted in Fortran Arrays? . In that question it was caused by the bad assignment syntax while initializing an array, but in this case I'm completely clueless why my output looks like this:
1 2
1 2
Write into binary
Read from binary
array size: 2
b
1 0
c
3
The values under b should be obviously 1 and 2, not 1 and 0. What am I doing wrong?
The problem here is that in
select type(t => arr(i)%item)
type is(b)
write(123) t
end select
ifort is not actually selecting the procedure write_b to process the defined output. It is also not selecting the procedure read_b when it comes to processing the defined input.
Instead the procedures write_a and read_a are selected.
This is a problem with the compiler and should be reported to Intel. A rather tedious workaround is to select type in those procedures.

Breaking up a Module into Multiple Files in Fortran

I'm attempting to add conversion functions to between different derived types. I'm
wanting them to be functions of the first derived type that returns the other derived type. This is no problem as long as they are in the same file and module. But I'd really like them to be able to be separated into multiple files since otherwise it would be a very large file. I can't figure out how to do this because of dependencies, and the lack of namespace in Fortran.
Is there a way to do this?
Here is an example of what I'd like to be dividing into two files (one for each derived type).
Module ConversionStuff
implicit none
type A_T
real :: a, b, c
contains
procedure :: toX => a_toX
end type A_T
type X_T
real :: x, y, z
contains
procedure :: toA => x_toA
end type X_T
contains
function a_toX(this) result(x)
class(A_T), intent(inout) :: this
type(X_T) :: x
x%x = this%a * 2
x%y = this%b * 2
x%z = this%c * 2
end function a_toX
function x_toA(this) result(a)
class(X_T), intent(inout) :: this
type(A_T) :: a
a%a = this%x * 0.5
a%b = this%y * 0.5
a%c = this%z * 0.5
end function x_toA
End Module ConversionStuff
I do apologize if there is a typo. I don't have an easy way to compile Fortran on this computer.
Within the current language this is easily enough dealt with through submodules - both type definitions go into the ancestor module along with the interfaces for the "shared" separate module procedures, procedure definitions are then split between submodules as it suits.
MODULE ConversionStuff
IMPLICIT NONE
TYPE :: A_T
REAL :: a, b, c
CONTAINS
PROCEDURE :: toX => a_toX
END TYPE A_T
TYPE :: X_T
REAL :: x, y, z
CONTAINS
PROCEDURE :: toA => x_toA
END TYPE x, y, z
INTERFACE
MODULE FUNCTION a_toX(this) RESULT(x)
IMPLICIT NONE
CLASS(A_T), INTENT(IN) :: this
TYPE(X_T) :: x
END FUNCTION a_toX
MODULE FUNCTION x_toA(this) RESULT(a)
IMPLICIT NONE
CLASS(X_T), INTENT(IN) :: this
TYPE(A_T) :: a
END FUNCTION x_toA
END INTERFACE
END MODULE ConversionStuff
SUBMODULE (ConversionStuff) Procedures_for_X
IMPLICIT NONE
CONTAINS
MODULE PROCEDURE a_toX
x%x = this%a * 2
x%y = this%b * 2
x%z = this%c * 2
END PROCEDURE a_toX
END SUBMODULE Procedures_for_X
...
Prior to Fortran 2008, you can sometimes use an alternative approach that emulates the above - the implementation of the procedures goes in a separately compiled set of external procedures that use the module. Care needs to be taken that the external procedures do not have visibility of their own interface.
MODULE ConversionStuff
IMPLICIT NONE
TYPE :: A_T
REAL :: a, b, c
CONTAINS
PROCEDURE :: toX => a_toX
END TYPE A_T
TYPE :: X_T
REAL :: x, y, z
CONTAINS
PROCEDURE :: toA => x_toA
END TYPE x, y, z
INTERFACE
FUNCTION a_toX(this) RESULT(x)
IMPORT :: A_T
IMPORT :: X_T
IMPLICIT NONE
CLASS(A_T), INTENT(IN) :: this
TYPE(X_T) :: x
END FUNCTION a_toX
FUNCTION x_toA(this) RESULT(a)
IMPORT :: A_T
IMPORT :: X_T
IMPLICIT NONE
CLASS(X_T), INTENT(IN) :: this
TYPE(A_T) :: a
END FUNCTION x_toA
END INTERFACE
PRIVATE :: a_toX
PRIVATE :: x_toA
END MODULE ConversionStuff
FUNCTION A_toX(this) RESULT(x)
USE ConversionStuff
IMPLICIT NONE
CLASS(A_T), INTENT(IN) :: this
TYPE(X_T) :: x
...etc...
END FUNCTION A_toX
There are limitations for the use of Fortran's accessibility attributes (PUBLIC and PRIVATE) for this second approach.
Note that the problem has nothing to do with namespaces, as the concept is typically defined.
After working on other things and coming back to this almost 2 months later. I found something I think is far more simple and elegant for this specific usage. I'm going to leave the previously accepted answer because it definitely answers the question, but this is an alternative method.
It uses the include keyword. I didn't understand until now that it doesn't compile included file until it is compiling the including file. Maybe there is something I don't understand, but for this situation where I only want to split into multiple files so that my single file isn't enormous, I think this method is worth the trade off in what I lose for it not being a module. If there is something I'm missing please let me know.
My solution is below.
Module ConversionStuff
implicit none
type A_T
real :: a, b, c
contains
procedure :: toX => a_toX
end type A_T
type X_T
real :: x, y, z
contains
procedure :: toA => x_toA
end type X_T
contains
include "A.f90"
include "X.f90"
End Module ConversionStuff
A.f90
function a_toX(this) result(x)
class(A_T), intent(inout) :: this
type(X_T) :: x
x%x = this%a * 2
x%y = this%b * 2
x%z = this%c * 2
end function a_toX
X.f90
function x_toA(this) result(a)
class(X_T), intent(inout) :: this
type(A_T) :: a
a%a = this%x * 0.5
a%b = this%y * 0.5
a%c = this%z * 0.5
end function x_toA

Fortran 90, how to use array defined in derived type in a subroutine

I defined a derived type as follows:
TYPE CLST_MEAN
REAL(8), ALLOCATABLE :: OMX(:,:), OMZ(:,:)
REAL(8), ALLOCATABLE :: U(:,:), W(:,:)
REAL(8), ALLOCATABLE :: YO(:,:), ZO(:,:)
REAL(8), ALLOCATABLE :: XU(:,:), ZU(:,:)
INTEGER :: NUM
END TYPE Clst_Mean
In the main code, I defined an array and input it in a subroutine as
TYPE(CLST_MEAN), ALLOCATABLE :: MEAN(:)
ALLOCATE(MEAN(NCL))
DO I = 1, NCL
ALLOCATE(MEAN(I)%OMX(NY,NZ))
ALLOCATE(MEAN(I)%OMZ(NY,NZ))
ALLOCATE(MEAN(I)%YO(NY,NZ))
ALLOCATE(MEAN(I)%ZO(NY,NZ))
ALLOCATE(MEAN(I)%U(NX,NZ))
ALLOCATE(MEAN(I)%W(NX,NZ))
ALLOCATE(MEAN(I)%XU(NX,NZ))
ALLOCATE(MEAN(I)%ZU(NX,NZ))
END DO
CALL K_MEAN(MEAN,SMP)
In the subroutine,
SUBROUTINE K_MEAN(CL_MEAN,SMP)
USE DATATYPE, ONLY : CLST_MEAN, SAMPLE
IMPLICIT NONE
TYPE(CLST_MEAN), DIMENSION(:), INTENT(OUT) :: CL_MEAN
....
write(*,*) size(cl_mean), SIZE(CL_MEAN(1)%OMX)
The output for size(cl_mean) is correct. But the output of size(cl_mean(1)%omx) is 1. That means that the compiler considers cl_mean(1)%omx is a variable not an array.
How can I access the array? Thank you.
I think using intent(out) causes the allocatable array in the derived datatype to be deallocated. Using intent(inout) fixed the problem for me using gfortran 4.7.