FORTRAN: data polymorphic
I am trying to hide the difference between real and complex data types. In FORTRAN 2003 I think there may be a way to do this.
The goal is to define a polymorphic allocable array whose type can be determined at runtime. Plus there is a subroutine that takes a polymorphic array to do some algebra (the same equation works for real and complex data).
To do this, I made two attempts:
Approach A:
module poly
implicit none
private
type, abstract, public :: MyType
contains
procedure, public :: Constructor
endtype MyType
type, extends(MyType), public :: MyTypeR
real(8), allocatable :: AllData(:)
endtype MyTypeR
type, extends(MyType), public :: MyTypeI
complex(8), allocatable :: AllData(:)
endtype MyTypeI
contains
subroutine Constructor(this, Nsize)
class(MyType), intent(inout) :: this
integer, intent(in) :: Nsize
select type(this)
type is(MyTypeR)
allocate(this%AllData(Nsize))
type is(MyTypeI)
allocate(this%AllData(Nsize))
endselect
endsubroutine
endmodule poly
! Algebra subroutine
module Operation
contains
subroutine Square(Array)
class(*), intent(inout) :: Array(:)
select type(Array)
class is(real(8))
Array = Array**2
class is(complex(8))
Array = Array**2
endselect
endsubroutine Square
endmodule Operation
! Main
program test
use poly
use Operation
class(MyType), allocatable :: t1, t2
integer :: i
logical :: IfComplex = .true.
if(IfComplex) then
allocate(MyTypeI::t1)
else
allocate(MyTypeR::t1)
endif
call t1%Constructor(4)
call Square(t1%AllData)
endprogram test
Approach B (unrestricted polymorphic allocable variable):
module poly
implicit none
private
type, public :: MyType
class(*), allocatable :: AllData(:)
contains
procedure, public :: Constructor
endtype MyType
contains
subroutine Constructor(this, Nsize, IfComplex)
class(MyType), intent(inout) :: this
integer, intent(in) :: Nsize
logical, intent(in) :: IfComplex
if(IfComplex) then
allocate(complex(8)::this%AllData(Nsize))
else
allocate(real(8)::this%AllData(Nsize))
endif
endsubroutine
endmodule poly
! Same algebra subroutine
! Main
program test
use poly
use Operation
type(MyType) :: t1, t2
integer :: i
call t1%Constructor(4, .true.)
call Square(t1%AllData)
endprogram test
Then I have a problem with the algebra subroutine in both approaches: in the internal assignment, the variable should not be polymorphic. Any suggestion would be appreciated.
source to share
There are several problems.
In current Fortran, you cannot extend internal types. Internal types cannot appear in the EXTENDS specifier in a derived type definition.
Therefore, in Fortran 2008, the language prohibits pretending that internal types can be an ancestor type in a construct of the selected type. This prohibition is implied in the syntax rules for an instruction of type guard (TYPE IS ... CLASS IS, etc. - the CLASS IS class of type -guard-stmt is explicitly limited to a derived type specification, which excludes the use of built-in type names), which means that the corresponding Fortran 2008 compiler should issue an error message for your syntax.
(This limitation was not published in Fortran 2003, but it was added in a later Fortran 2003 patch - your Fortran 2003 compiler vendor may not have implemented it yet.)
In Fortran 2003, internal assignment was not allowed when the variable being assigned (item to the left of equals) was polymorphic. The ability to assign a polymorphic variable was added to the language in Fortran 2008.
The solution to both of the above problems is to make statements like guard in your Square
TYPE IS routine , not in CLASS IS.
Apart from this immediate problem (the following is more subjective and depends on what you ultimately plan to do):
-
In the first example, a more typical layout would be that there are two separate non-type constructor procedures, one for MyTypeR and one for MyTypeI. The algebraic operation would then be the deferred binding of the parent of MyType, which then extends it.
-
MyType in the second example doesn't really play a useful role - you can also use distributed unbounded polymorphic objects.
source to share