mcocdawc
mcocdawc

Reputation: 1867

Overload for different types and their baseclass

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:

  1. 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.

  2. If the answer to question 1 is: "There is no conceptional ambiguity". Are there plans in the standard to change this?

  3. 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

Answers (1)

IanH
IanH

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

Related Questions