How do we select procedures within classes? The compilation error is:
$ gfortran -Wall -Wextra -Wconversion -Og -pedantic -fcheck=bounds -fbacktrace -g -fmax-errors=5 driver.f08
driver.f08:60:39:
call sub ( me, myMeasurements )
1
Error: More actual than formal arguments in procedure call at (1)
which occurs in subroutine local_selector_sub
(line 48) within module mIntermediates
(line 28). The argument sub
will be either compute_intermediates_dot
or compute_intermediates_sum
.
The MWE code driver.f08
tries to select a computation path. Calls to the two choices, compute_intermediates_sum
and compute_intermediates_dot
both work (lines 93, 94).
The problem is the call to local_selector
(line 95) which contains the name of the target computation path. The goal is have different manifestations such as
call myIntermediates % local_selector( compute_intermediates_sum, myMeasurements )
or
call myIntermediates % local_selector( compute_intermediates_dot, myMeasurements )
MWE:
module mMeasurements ! * * *
implicit none
integer, parameter :: m = 2
type :: measurements
real, dimension ( 1 : m ) :: x = 0.0, y = 0.0, ones = 1.0, residuals = 0.0
contains
private
procedure, public :: load_data
end type measurements
contains
subroutine load_data ( me )
class ( measurements ), target :: me
me % x ( 1 ) = 1.0 ! load data
me % x ( 2 ) = 2.0
me % y ( 1 ) = 15.6
me % y ( 2 ) = 17.5
end subroutine load_data
end module mMeasurements
module mIntermediatesDefinitions ! * * *
use mMeasurements
implicit none
type :: intermediates
real :: sxy = 0.0
contains
private
procedure, public :: compute_intermediates_dot => compute_intermediates_dot_sub
procedure, public :: compute_intermediates_sum => compute_intermediates_sum_sub
procedure, public :: local_selector => local_selector_sub
end type intermediates
private :: local_selector_sub
private :: compute_intermediates_dot_sub
private :: compute_intermediates_sum_sub
contains
subroutine local_selector_sub ( me, sub, myMeasurements ) ! problematic routine
class ( intermediates ), target :: me
type ( measurements ), intent ( in ) :: myMeasurements
interface mySub
subroutine sub
use mMeasurements
import intermediates
end subroutine sub
end interface mySub
call sub ( me, myMeasurements )
end subroutine local_selector_sub
subroutine compute_intermediates_dot_sub ( me, myMeasurements )
class ( intermediates ), target :: me
type ( measurements ), intent ( in ) :: myMeasurements
me % sxy = dot_product ( myMeasurements % x, myMeasurements % y )
end subroutine compute_intermediates_dot_sub
subroutine compute_intermediates_sum_sub ( me, myMeasurements )
class ( intermediates ), target :: me
type ( measurements ), intent ( in ) :: myMeasurements
me % sxy = sum ( myMeasurements % x * myMeasurements % y )
end subroutine compute_intermediates_sum_sub
end module mIntermediatesDefinitions
program driver ! # # #
use mMeasurements
use mIntermediatesDefinitions
implicit none
type ( measurements ) :: myMeasurements
type ( intermediates ) :: myIntermediates
call myIntermediates % compute_intermediates_dot ( myMeasurements )
call myIntermediates % compute_intermediates_sum ( myMeasurements )
call myIntermediates % local_selector ( compute_intermediates_dot, myMeasurements )
end program driver
Version:
GNU Fortran (GCC) 5.1.0
The dummy procedure sub
in subroutine local_selector_sub
has interface given by the interface block. That interface
interface
subroutine sub
end subroutine sub
end interface
(with the redundant use
and import
and the unused generic name removed) says that sub
has no arguments. In the following call you attempt to give it two arguments, and the compiler rightly complains about that.
To fix that you will need to specify the interface correctly (or rely on an implicit interface). As I note that both options compute_intermediates_dot_sub
and compute_intermediates_sum_sub
have the same characteristics you could write, for example
subroutine local_selector_sub ( me, sub, myMeasurements ) ! problematic routine
class ( intermediates ), target :: me
type ( measurements ), intent ( in ) :: myMeasurements
procedure(compute_intermediates_dot_sub) sub
call sub ( me, myMeasurements )
end subroutine
(or you could create a neutrally named abstract interface or use an appropriate interface block).
But that leads to a much more interesting problem:
call myIntermediates%local_selector(compute_intermediates_dot, myMeasurements)
compute_intermediates_dot
is not a subroutine. It is a binding name for the type bound procedure of type intermediates
.
The simplest solution if you wish to pass a subroutine to myIntermediates%local_selector
is to make the subroutine compute_intermediates_dot_sub
itself accessible/public and pass that. But I'd be tempted to stick with choosing between the other two lines.
Alternatively, if you really wish to select a subroutine to pass to myIntermediates%local_selector
then you could consider having procedure pointer components rather than type-bound procedures.