Home > Enterprise >  Fortran : Is it possible to overload a procedure in a abstract interface?
Fortran : Is it possible to overload a procedure in a abstract interface?

Time:07-12

I wrote the following code where an abstract type (shape_t) could be extended to a segment, a rectangle or a box. There are two procedures, one to initialize the variable and the other to print the size. This code gave the expected result.
A segment, a rectangle or a box could be initialized and the size could be printed.

module myModule
    implicit none
    
    type, abstract :: shape_t
    contains
        procedure(abstract_init), deferred :: 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 :: init => init_line
        procedure :: print_size => print_linesize
    end type line_t
   
     type, extends(shape_t) :: rectangle_t
        real :: length,width
    contains
        procedure :: init => init_rectangle
        procedure :: print_size => print_rectanglesize
    end type rectangle_t
    
    type, extends(shape_t) :: box_t
        real :: length,width,height
    contains
        procedure :: init => init_box
        procedure :: print_size => print_boxsize
    end type box_t
    
contains
    subroutine init_line(this,length1,length2,length3)
        class(line_t), intent(inout) :: this
        real, intent(in) :: length1,length2,length3
        this%length = 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
        this%length = length1
        this%width = length2
    end subroutine init_rectangle
       
    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(line_t), allocatable :: segment
    class(rectangle_t), allocatable :: rectangle
    class(box_t), allocatable :: box
    
    allocate(line_t::segment)
    call segment%init(12.0,0.0,0.0)
    call segment%print_size()
    
    allocate(rectangle_t::rectangle)
    call rectangle%init(12.0,5.0,0.0)
    call rectangle%print_size()
    
    allocate(box_t::box)
    call box%init(12.0,5.0,2.0)
    call box%print_size()
    
end program main

But as there are no the same number of dimensions between segment, rectangle and box and I do not know how to overload a procedure in a abstract interface, I use 3 real parameters to initialise variables (even for segment and rectangle where only one or two are needed, respectively). I wonder how is it possible to modify this code, to change in the main program :
call segment%init(12.0,0.0,0.0) and call rectangle%init(12.0,5.0,0.0) to call segment%init(12.0) and call rectangle%init(12.0,5.0)
Thanks for answer.

CodePudding user response:

The deferred procedure in the abstract interface needs to have the same interface in all extended classes. This is unfortunate, but Fortran has no easy way to work around this. I think you have two options:

  1. In a relatively simple case like this, you could set some of the arguments to optional, and then check inside each class implementation that all inputs you need have already been provided, like:
        subroutine abstract_init(this,length1,length2,length3)
            import shape_t
            class(shape_t), intent(inout) :: this
            real, intent(in) :: length1
            real, intent(in), optional :: length2,length3
        end subroutine abstract_init
  1. Otherwise, you can have a generic interface to init. In other words, you're going to write more wrappers to the initialization function, that allow for different inputs. This is an example:
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(line_t), allocatable :: segment
    class(rectangle_t), allocatable :: rectangle
    class(box_t), allocatable :: box

    allocate(line_t::segment)
    call segment%init(12.0)
    call segment%print_size()

    allocate(rectangle_t::rectangle)
    call rectangle%init(12.0,5.0)
    call rectangle%print_size()

    allocate(box_t::box)
    call box%init(12.0,5.0,2.0)
    call box%print_size()

end program main

In other words, the deferred procedure always represents a shared common procedure to all instances. Also keep in mind that you can always use default initializers, like:

allocate(segment  ,source=line_t(length=12.0))
allocate(rectangle,source=rectangle_t(length=12.0,width=5.0))
allocate(box      ,source=box_t(length=12.0,width=5.0,depth=2.0))
  • Related