Reputation: 1867
Let's assume I have an abstract baseclass Shape_t
with derived types Rectangle_t
and Circle_t
.
There is a generic function get_area
for both derived types and I would like to overload it as well for the class so that I get the following interfaces (Julianesque notation):
get_area(type(Circle_t) :: C)
get_area(type(Rectangle_t) :: R)
! The following leads to ambiguous interfaces
get_area(class(Shape_t) :: S)
Unfortunately I get an "ambiguous interface" error, when I try this. From this I have three questions:
Is there anything conceptionally wrong with what I want to achieve? Since variables are explicitly declared as polymorphic (class(...)
) the compiler can always choose the most specific interface and fall back to the polymorphic interface. So I don't see the ambiguity.
If the answer to question 1 is: "There is no conceptional ambiguity". Are there plans in the standard to change this?
Is the following code, where a dyn_get_area
for dynamic polymorphism is introduced, a solid workaround? Note that I would like to stick to static polymorphism as long as possible, i.e. as long as the concrete Shape is known at compile time.
module shapes_mod
implicit none
private
public :: Shape_t, Rectangle_t, Circle_t, PI, get_area, dyn_get_area
real, parameter :: PI = atan(1.0) * 4.0
type, abstract :: Shape_t
end type
type, extends(Shape_t) :: Circle_t
real :: r = 0.0
end type
type, extends(Shape_t) :: Rectangle_t
real :: a = 0.0, b = 0.0
end type
interface get_area
module procedure get_area_Rectangle_t, get_area_Circle_t
end interface
contains
pure function get_area_Circle_t(C) result(res)
type(Circle_t), intent(in) :: C
real :: res
res = C%r**2 * PI
end function
pure function get_area_Rectangle_t(R) result(res)
type(Rectangle_t), intent(in) :: R
real :: res
res = R%a * R%b
end function
pure function dyn_get_area(S) result(res)
class(Shape_t), intent(in) :: S
real :: res
select type(S)
type is(Rectangle_t)
res = get_area(S)
type is(Circle_t)
res = get_area(S)
end select
end function
end module
program test_polymorphic_and_static_overload
use shapes_mod, only: Shape_t, Rectangle_t, Circle_t, get_area, dyn_get_area
implicit none
class(Shape_t), allocatable :: random_shape
type(Circle_t) :: circle
type(Rectangle_t) :: rectangle
real :: p
circle = Circle_t(1.0)
rectangle = Rectangle_t(1.0, 2.0)
call random_number(p)
if (p < 0.5) then
random_shape = circle
else
random_shape = rectangle
end if
write(*, *) get_area(circle)
write(*, *) get_area(rectangle)
write(*, *) dyn_get_area(random_shape)
end program
Upvotes: 1
Views: 80
Reputation: 21431
Fortran's rules for selection of a specific procedure from a generic interface, and for how specific procedures in a generic interface must be distinguishable, are designed to be simple to learn and apply. To avoid the need for any rules describing how a "best match"/"most specific" is selected, in circumstances that might otherwise be ambiguous, the language rules are such that there can be at most one non-elemental procedure and at most one elemental procedure that matches, in any scoping unit (elemental specifics are only considered if a non-elemental specific does not match).
Your proposed procedures violate these language rules.
I have not seen any realistic proposals to change the rules in a way that would lose the "simple to learn and apply" design intent.
Put a deferred binding in each type that nominates a function that implements the relevant calculation for get_area. Forward to that binding from the get_shape function (or change references to get_shape to be references to the binding). Avoid implementing type specific behaviour using SELECT TYPE - type specific behaviour should be implemented using bindings.
Upvotes: 4