问题
How do I point to a type-bound procedure? Say that I have some external subroutine that takes as an argument a pointer to a function that accepts only one argument.
call integrator(f0)
This will work if the function f0 is defined somewhere so that it looks like
function f0(x) result(val)
... do something...
end function
But now I have a type SomeClass with some type-bound procedures. One of these type-bound procedures is
function integrand(this,x) result(val)
class(SomeClass), intent(in) :: this
...do something...
end function
And I have another type-bound procedure in the same type that wants to call the subroutine above passing the first type-bound procedure to it, but I have no idea how to go about writing it! Let me first try the somewhat naive,
function CalculateIntegral(this) result(val)
class(SomeClass), intent(in) :: this
call integrator(this%integrand)
end function
This gives me
call integrator(this%integrand)
1
Error: Expected argument list at (1)
which I learnt from this discussion is because this%integrand does not return a pointer to the function but is a binding to the function.
So, now I'll try this
function CalculateIntegral(this) result(val)
class(SomeClass), intent(in) :: this
call integrator(integrand)
end function
and it compiles but gives me a Memory Reference error because it is trying to pass the value (x) to something that is meant for class(SomeClass) kind of argument (viz., this).
So if this%integrand only gives me a binding and not a pointer to a type-bound member procedure, how do I pass one of my type-bound member procedures to an external subroutine without the first "this" argument getting in the way?
NOTE: I'm used to coding in python, where self.integrand could be passed to the external function and everything would be fine.
EDIT: My bad; I remembered wrong. Python has the same problem if you try to pass self.integrand to an external function.
回答1:
Following the links in @Vladimir's comment, passing an internal procedure seems to work for recent compilers (Note: I think this first example is the simplest and the best among the codes below):
module mymod
implicit none
type mytype
real :: q
contains
procedure :: calc_integral
end type
contains
function calc_integral( this, a, b ) result( ans )
class(mytype) :: this
real :: a, b, ans
call integrator( myfunc, a, b, ans )
contains
function myfunc( x ) result( val )
real :: x, val
val = this% q * x
end function
endfunction
end module
!! Some external library.
subroutine integrator( func, a, b, ans )
external :: func
real :: a, b, ans, func
ans = func( a ) + func( b )
end
program main
use mymod
type(mytype) :: mt
mt% q = 100.0
print *, mt% calc_integral( 1.0, 2.0 ) !! gives 300.0
end
In the above code it is also possible to pass a module procedure to integrator()
(rather than passing an internal procedure), but in that case something like this_
may be necessary for myfunc()
to access the type components (see the next case).
Below is another attempt to pass the integrand in a different way.
Here, a pointer to a module procedure myfunc()
is passed to integrator()
module mymod
implicit none
interface
function integrand_i( x ) result( val )
real :: x, val
endfunction
endinterface
type mytype
real :: q
procedure(integrand_i), nopass, pointer :: integrand
contains
procedure :: init
endtype
class(mytype), pointer :: this_
contains
subroutine init( this )
class(mytype), target :: this
this% integrand => myfunc
this_ => this
endsubroutine
function myfunc( x ) result( val )
real :: x, val
val = this_ % q * x
endfunction
end module
! subroutine integrator() here
program main
use mymod
type(mytype) :: mt
real :: ans
call mt% init
mt% q = 100.0
call integrator( mt% integrand, 1.0, 2.0, ans )
print *, ans
end
which looks somewhat similar to the OP's original approach in the Question (in my opinion).
Another (not working) approach is to modify the above init()
so that it obtains a pointer to an internal procedure
subroutine init( this )
class(mytype) :: this
this% integrand => myfunc
contains
function myfunc( x ) result( val )
real :: x, val
val = this % q * x
endfunction
endsubroutine
but this does not work because the internal procedure becomes undefined when exiting init(). Similarly, the following seems not working either (it doesn't even compile with gfortran4.8.2).
type mytype
...
contains
procedure :: integrand
endtype
...
function integrand( this ) result( ptr )
class(mytype) :: this
procedure(integrand_i), pointer :: ptr
ptr => myfunc
contains
function myfunc( x ) result( val )
real :: x, val
val = this % q * x
endfunction
endfunction
program main
...
call integrator( mt% integrand, 1.0, 2.0, ans )
来源:https://stackoverflow.com/questions/31644810/fortran-passing-a-member-procedure-to-an-external-function