Home > Software design >  Can I overload a type-bound procedure based on the number of arguments?
Can I overload a type-bound procedure based on the number of arguments?

Time:09-17

I'm working on an aerodynamics code (source code here) using good old Fortran. Part of the initialization is reading in a file which contains information on a surface mesh, basically a set of triangular or quadrilateral panels making up the surface. I'd like to describe both quadrilateral and triangular panels using a single type panel. To do that, I've written two initialization functions panel_init_3 and panel_init_4 which will load the data into the type based on how many vertices the panel has. I'd like to bind these to my panel type and overload them based on the number of arguments (i.e. if it gets passed an integer and 3 vertex objects, then panel_init_3 gets called and similarly for 4 vertex objects.

Here is the source code for the type:

module panel_mod

    use linked_list_mod
    use vertex_mod

    implicit none


    type panel
        ! A panel with an arbitrary number of sides

        integer :: N ! Number of sides/vertices
        type(vertex_pointer),dimension(:),allocatable :: vertices
        real,dimension(3) :: n_hat ! Normal vector
        real :: A ! Surface area

        contains

            procedure :: init => panel_init_3, panel_init_4
            procedure :: calc_area => panel_calc_area
            procedure :: calc_normal => panel_calc_area

    end type panel

    
contains


    subroutine panel_init_3(this, v1, v2, v3)
        ! Initializes a 3-panel

        implicit none

        class(panel),intent(inout) :: this
        type(vertex),intent(in),target :: v1, v2, v3

        ! Set number of sides
        this%N = 3

        ! Allocate vertex array
        allocate(this%vertices(this%N))

        ! Store info
        this%vertices(1)%ptr => v1
        this%vertices(2)%ptr => v2
        this%vertices(3)%ptr => v3

        ! Calculate normal vec

        ! Calculate area

    end subroutine panel_init_3


    subroutine panel_init_4(this, v1, v2, v3, v4)
        ! Initializes a panel with 4 sides

        implicit none

        class(panel),intent(inout) :: this
        type(vertex),intent(in),target :: v1, v2, v3, v4
        
        ! Set number of sides
        this%N = 4

        ! Allocate vertex array
        allocate(this%vertices(this%N))

        ! Store info
        this%vertices(1)%ptr => v1
        this%vertices(2)%ptr => v2
        this%vertices(3)%ptr => v3
        this%vertices(4)%ptr => v4

        ! Calculate normal vector

        ! Calculate area

    end subroutine panel_init_4


    subroutine panel_calc_area(this)

        implicit none

        class(panel),intent(inout) :: this

    end subroutine panel_calc_area


    subroutine panel_calc_normal(this)

        implicit none

        class(panel),intent(inout) :: this

    end subroutine panel_calc_normal

    
end module panel_mod

This module compiles fine. But, when it gets used here

...
! Initialize triangular panel
if (N == 3) then
   call panels(i)%init(vertices(i1 1), vertices(i2 1), vertices(i3 1)) ! Need  1 because VTK is 0-indexed

! Initialize quadrilateral panel
else
   call panels(i)%init(vertices(i1 1), vertices(i2 1), vertices(i3 1), vertices(i4 1))
end if
...

I get the compiler message

   70 |                     call panels(i)%init(vertices(i1 1), vertices(i2 1), vertices(i3 1), vertices(i4 1))
      |                                                                                                       1
Error: More actual than formal arguments in procedure call at (1)

Am I reaching beyond the capabilities of Fortran, or is there a way to do this? I realize I could do this all without binding and overloading, but I like how clean it'd be. I am using gfortran 9.3.0.

CodePudding user response:

In a type-bound procedure declaration statement it's possible to declare more than one binding name in a list, so we can have

type mytype
contains
  procedure :: binding1, binding2
end type

giving the two binding names. These are individual bindings. These binding names resolve to procedures with the same name as each. That is, we can write this as

type mytype
contains
  procedure :: binding1 => binding1, binding2 => binding2
end type

The list nature of the binding names is such that

type mytype
contains
  procedure :: binding => binding1, binding2
end type

has the items in the list binding=>binding1 and binding2. This has effect like

type mytype
contains
  procedure :: binding => binding1, binding2 => binding2
end type

That is, we still have two distinct binding names each binding to a single procedure.

We do not have a generic binding binding name with specific procedures binding1 and binding2. For that, we'd need a generic explicitly created:

type mytype
contains
  procedure :: binding1, binding2
  generic :: binding => binding1, binding2
end type

In this generic statement, the list has binding1 and binding2 as items as specifics for the generic binding. Precise syntax rules for these lists/statements is given in Fortran 2018 7.5.5.

CodePudding user response:

It is possible, but you need to define init as a generic procedure (binding) with those other two being the specific procedures (bindings)

   contains

        procedure :: panel_init_3
        procedure :: panel_init_4
        generic :: init => panel_init_3, panel_init_4

It is quite similar to what is normally done with normal procedures using named interface blocks when you do

interface generic_init
  procedure specific_init_3
  procedure specific_init_4
end interface
  • Related