Fortran: Passing a member procedure to an external function

让人想犯罪 __ 提交于 2021-01-27 20:11:56

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!