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
.