interfacefortranfortran90derived-types

Type-bound procedure that uses an external procedure with an explicit interface: Sometimes it compiles, sometimes not


I screwed up on my previous question and had to delete it. Here's a new one:

I got most of this code from this quite helpful site:

module shape_mod

type shape
    integer :: color
    logical :: filled
    integer :: x
    integer :: y
contains
    procedure :: initialize
end type shape

type, extends(shape) :: rectangle
        integer :: length
        integer :: width
end type rectangle

type, extends(rectangle) :: square
end type square

interface
    subroutine initialize(sh, color, filled, x, y, length, width)
        import shape
        class(shape) :: sh
        integer :: color
        logical :: filled
        integer :: x
        integer :: y
        integer, optional :: length
        integer, optional :: width
  end subroutine
end interface

end module

subroutine initialize(sh, color, filled, x, y, length, width)

    ! initialize shape objects
    class(shape) :: sh
    integer :: color
    logical :: filled
    integer :: x
    integer :: y
    integer, optional :: length
    integer, optional :: width

    ! do stuff with shape

end subroutine initialize


program drv

use shape_mod

type(shape) :: sh

call sh%initialize(1, .true., 0, 0, 5, 10)

end program

This fails to compile (as it should, as pointed out by respondents to my previous question) with the error:

gfortran shape2.f90
shape2.f90:38:16:

     class(shape) :: sh
                1
Error: Derived type ‘shape’ at (1) is being used before it is defined
shape2.f90:46:7: Error: Symbol ‘sh’ at (1) has no IMPLICIT type
shape2.f90:47:7: Error: Symbol ‘sh’ at (1) has no IMPLICIT type
shape2.f90:48:7: Error: Symbol ‘sh’ at (1) has no IMPLICIT type
shape2.f90:49:7: Error: Symbol ‘sh’ at (1) has no IMPLICIT type

So, my question is, what can I do to get subroutine initialize() to know about type shape? The only thing I can think of is to put a use statement in:

subroutine initialize(sh, color, filled, x, y, length, width)

    use shape_mod
    ! initialize shape objects
    class(shape) :: sh
    integer :: color
    logical :: filled
    ...
end subroutine initialize

But that gives me a new error:

gfortran shape2.f90
shape2.f90:37:8:

     use shape_mod
        1
Error: ‘initialize’ of module ‘shape_mod’, imported at (1), is also the name of the current program unit

How to write the subroutine is the one thing the link I referenced above does not tell. Is there a way to do this? Or does initialiaze() have to be part of shape_mod for this to work?


Solution

  • In the module you have defined an interface for the external procedure initialize. When you use this module in the subroutine definition you have access to the interface of the subroutine itself.

    You cannot do this.

    Fortunately, you can avoid having the interface accessible by

    use shape_mod, only shape
    

    Now, the above is necessary because of the design made to use an external procedure in the type binding. In general, one would expect not to use an external procedure in this way. As we've seen, there's the additional complexity in using an external procedure, both with having to use the module where the type is defined, but also in having to manually specify the procedure's interface.

    There are times where an external interface would be useful, but here the purpose of the example leading to the question was perhaps pedagogical rather than simple. There's no obvious reason here why initialize shouldn't be a module procedure.

    Instead, consider the example

    interface
      subroutine ext()
      end subroutine
    end interface
    
    type mytype
     contains
      procedure(iface), nopass :: ext
    end type
    

    The external subroutine ext doesn't have a passed-object dummy (the binding has nopass) so doesn't need the module in which mytype is defined. This is a simplification.

    Finally, as High Performance Mark comments, perhaps initialize needn't even be a binding name. Instead a "constructor" could be used:

    type mytype
    end mytype
    
    interface mytype
      procedure intialize_mytype
    end interface
    

    Details are left for the interested reader to find from other sources.