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