3
votes

I am trying to hide the difference between the real and complex data types. In FORTRAN 2003 I think there could be a way of doing so.

The goal is to define a polymorphic allocatable array which type can be decided at the run-time. Plus there is a subroutine which takes the polymorphic array to do some algebra (same equation works for real and complex data).

In order to do it, 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 (unlimited polymorphic allocatable 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 am having problem with the algebra subroutine in both approaches: In an intrinsic assignment statement, variable shall not be polymorphic. Any suggestion will be appreciated.

1

1 Answers

5
votes

There are a few issues.

In current Fortran, you cannot extend the intrinsic types - the intrinsic types cannot appear in an EXTENDS specifier in a derived type definition.

Consequently, in Fortran 2008 the language prohibits pretending that the intrinsic types can be an ancestor type in a select type construct. This prohibition is implicit in the syntax rules for the type guard statement (TYPE IS... CLASS IS, etc - the CLASS IS form of type-guard-stmt is explicitly limited to a derived-type-spec, which excludes the use of intrinsic type names), which means that a conforming Fortran 2008 compiler should issue an error message for your syntax.

(That restriction didn't exist in Fortran 2003 as published, but it was added in a later Fortran 2003 corrigendum - perhaps your Fortran 2003 compiler vendor hasn't got around to implementing it yet.)

In Fortran 2003, intrinsic assignment was not allowed when the variable being assigned to (the thing on the left hand side of the equals) was polymorphic. The ability to assign to a polymorphic variable was a feature added to the language in Fortran 2008.

The solution to both the above issues is to make the type guard statements in your Square subroutine TYPE IS rather than CLASS IS.

Beyond that immediate issue (the following is more subjective and depends on what you are ultimately planning to do):

  • In the first example a more typical arrangement would be for there to be two separate non-type bound constructor procedures, one for MyTypeR and one for MyTypeI. The algebraic operation would then be a deferred binding of the MyType parent, which the extensions then implement appropriately.

  • MyType in the second example isn't really performing a useful role - you might as well be using allocatable unlimited polymorphic objects directly.