Search code examples
fortranderived-types

Two derived types sharing same subroutine


I have two derived types in a module, Interval and Inrset. Both have same functionality, however one operates on reals, the other on integers.

The problem being encountered is as follows

lib/interval.f:85:11:

Procedure :: eptset => inrval_set
        1
Error: Argument 't' of 'inrval_set' with PASS(t) at (1) 
must be of the derived-type 'inrset'

lib/interval.f:55:11:

Procedure :: eptset => inrval_set
        1
Error: Argument 't' of 'inrval_set' with PASS(t) at (1) 
must be of the derived-type 'interval'

Here is the Interval derived type

Type Interval

Real (Real32) :: inf, sup

Contains
  Procedure :: eptset => inrval_set

End Type Interval

And this is is the Intrset derived type

Type Inrset

Integer (Int32) :: inf, sup

Contains
  Procedure :: eptset => inrval_set

End Type Inrset

This shall be the common subroutine for setting inf and sup.

Subroutine inrval_set  &
  (                    &
    t, inf, sup        &
  )

Class (*), Intent (InOut) :: t
Class (*), Intent (In) :: inf, sup

!!$--------------------------------------------------
!!$ Sets t% inf = inf;  t% sup = sup

Select Type (t)
Type Is (Interval)
  Call numtrf (t% inf, inf)
  Call numtrf (t% sup, sup)

Type Is (Inrset)
  Call numtrf (t% inf, inf)
  Call numtrf (t% sup, sup)

End Select 

End Subroutine inrval_set

Solution

  • Error messages are, admittedly, not always helpful. In this case it is a good prompt.

    Your derived type Interval has a type-bound procedure with binding name eptset and interface the same as the procedure inrval_set. This is your difficulty.

    I imagine you have in mind a call like

    type(Interval) range
    call range%eptset(1._real32, 15._real32)
    

    which is a reasonable goal.

    However, you are relying on a passed-object dummy argument, so that in inrval_set your first dummy argument t is of dynamic type range. This is flawed.

    As the error message suggests, the interface for the type-bound procedure in Interval must, because it doesn't have the NOPASS attribute, have a dummy argument of type Interval. A dummy argument of class(*) is not such a thing.

    You don't want to do this approach using NOPASS.

    You could, of course, have

    call inrval_set(range, 1._real32, 15._real32)
    

    as one option. But are there type-bound ways?

    Yes.

    You could consider templating. Or, have an (abstract) parent class. Or provide type-bound procedures with the appropriate interface - one for each type.

    Essentially, you're repeating code in the select type block, so you may as well repeat the code with generic/dynamic resolution instead.