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