Home > Back-end >  OOP Fortran : Is it possible to avoid select type and call suboutine directly?
OOP Fortran : Is it possible to avoid select type and call suboutine directly?

Time:07-15

This question follows Fortran : Is it possible to overload a procedure in a abstract interface?.

Following the second option suggested by Federico Perini in its answer, I wrote the following code where a question is asked to the user to choose between line, rectangle or box.

module myModule
    implicit none

    type, abstract :: shape_t
    contains

        ! Put init in a generic interface
        procedure(abstract_init), deferred, private  :: shared_init
        generic :: init => shared_init

        procedure(abstract_print), deferred :: print_size
    end type shape_t

    abstract interface
        subroutine abstract_init(this,length1,length2,length3)
            import shape_t
            class(shape_t), intent(inout) :: this
            real, intent(in) :: length1,length2,length3
        end subroutine abstract_init

        subroutine abstract_print(this)
            import shape_t
            class(shape_t), intent(in) :: this
        end subroutine abstract_print
    end interface

    type, extends(shape_t) :: line_t
        real :: length
    contains
        procedure :: shared_init => init_line
        procedure :: init_line_1d
        generic   :: init => init_line_1d
        procedure :: print_size => print_linesize
    end type line_t

     type, extends(shape_t) :: rectangle_t
        real :: length,width
    contains
        procedure :: shared_init => init_rectangle
        procedure :: init_rectangle_2d
        generic   :: init => init_rectangle_2d
        procedure :: print_size => print_rectanglesize
    end type rectangle_t

    type, extends(shape_t) :: box_t
        real :: length,width,height
    contains
        procedure :: shared_init => init_box
        procedure :: print_size => print_boxsize
    end type box_t

contains
    subroutine init_line_1d(this,length1)
        class(line_t), intent(inout) :: this
        real, intent(in) :: length1
        this%length = length1
    end subroutine init_line_1d
    subroutine init_line(this,length1,length2,length3)
        class(line_t), intent(inout) :: this
        real, intent(in) :: length1,length2,length3
        call init_line_1d(this,length1)
    end subroutine init_line

    subroutine print_linesize(this)
        class(line_t), intent(in) :: this
        print*,'Line size',this%length,'meter'
    end subroutine print_linesize

    subroutine init_rectangle(this,length1,length2,length3)
        class(rectangle_t), intent(inout) :: this
        real, intent(in) :: length1,length2,length3
        call init_rectangle_2d(this,length1,length2)
    end subroutine init_rectangle
    subroutine init_rectangle_2d(this,length1,length2)
        class(rectangle_t), intent(inout) :: this
        real, intent(in) :: length1,length2
        this%length = length1
        this%width = length2
    end subroutine init_rectangle_2d

    subroutine print_rectanglesize(this)
        class(rectangle_t), intent(in) :: this
        print*,'Rectangle area',this%length*this%width,'meter^2'
    end subroutine print_rectanglesize

        subroutine init_box(this,length1,length2,length3)
        class(box_t), intent(inout) :: this
        real, intent(in) :: length1,length2,length3
        this%length = length1
        this%width = length2
        this%height = length3
    end subroutine init_box

    subroutine print_boxsize(this)
        class(box_t), intent(in) :: this
        print*,'Box volume',this%length*this%width*this%height,'meter^3'
    end subroutine print_boxsize

end module myModule

program main

    use myModule

    implicit none

    class(shape_t), allocatable :: dynamic_typing
    integer :: choice
    
    print *,'choice (1:line, 2:rectangle or 3:box)'
    read(*,'(i1)') choice
    select case (choice)
        case(1)
            allocate(line_t::dynamic_typing)
        case(2)
            allocate(rectangle_t::dynamic_typing)
        case(3)
            allocate(box_t::dynamic_typing)
        case default
            print *,'not permitted'
            stop
    end select
    
    select type (dynamic_typing)
        type is (line_t)
            call dynamic_typing%init(4.0)
            call dynamic_typing%print_size()
        type is (rectangle_t)
            call dynamic_typing%init(4.0,3.0)
            call dynamic_typing%print_size()
        type is (box_t)
            call dynamic_typing%init(4.0,3.0,2.0)
            call dynamic_typing%print_size()
    end select

end program main

But I am not able to avoid the select type part. I would like to avoid it and do the stuff DIRECTLY in the select case part. Something like that :

select case (choice)
    case(1)
        allocate(line_t::dynamic_typing)
        call dynamic_typing%init(4.0)
        call dynamic_typing%print_size()
    case(2)
        allocate(rectangle_t::dynamic_typing)
        call dynamic_typing%init(4.0,3.0)
        call dynamic_typing%print_size()
    case(3)
        allocate(box_t::dynamic_typing)
        call dynamic_typing%init(4.0,3.0,2.0)
        call dynamic_typing%print_size()
    case default
        print *,'not permitted'
        stop
end select

Is it possible or not ?

CodePudding user response:

Not. While you can't reference the type-bound procedures for a polymorphic object where the declared type is abstract, if you know the name of the specific procedure you can call it directly, passing the "this" argument explicitly.

The problem, though, is that the declared type of "this" in each of those procedures is an extension of the abstract type shape_t, and as such you can't pass an argument whose declared type is smaller than the extension. I discuss this in https://stevelionel.com/drfortran/2020/06/30/doctor-fortran-in-not-my-type/

Using select type makes the declared type, within each select block, be the named type and therefore you're allowed to pass it to the procedure.

Your code using select type is straightforward and clean.

  • Related