2

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.

dbrane
  • 887
  • 1
  • 7
  • 19
  • Almost a duplicate of http://stackoverflow.com/questions/31208594/how-to-pass-a-function-with-multiple-arguments-to-a-subroutine-that-expects-a-fu/31208717#31208717 the solution should apply here too. You should show what exactly you mean by the Python approach, there are more approaches to callbacks even in Python. See also http://www.fortran90.org/src/best-practices.html#callbacks – Vladimir F Героям слава Jul 27 '15 at 06:34
  • @VladimirF Will this thread be closed due to duplicate? If not yet determined, I will update my code below to ask opinions or better ways. If the possibility of duplicate is high, I will erase my code below.) – roygvib Jul 27 '15 at 07:19
  • Probably not, I don't know what is the opinion of others but I will not close it now. Maybe if there is a truly exact duplicate somewhere else, but my answer there states just one of more possibilities. – Vladimir F Героям слава Jul 27 '15 at 07:29

1 Answers1

2

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 )
roygvib
  • 7,218
  • 2
  • 19
  • 36
  • By reading the links in Vladimir's comment I tried several approaches, but except for the first one, I couldn't find how to remove "this_" pointer from the module... Btw, if time allows, could you put some simple example of Python code at the end of the question? (I'm still learning Python so would like to try it :) – roygvib Jul 27 '15 at 11:00
  • Note that there is no such thing as a "member" function in Fortran, hence "non-member" is meaningless. The reason why your third method fails is that the procedure pointer to the internal procedure becomes undefined when the instance of the host procedure completes. I don't see the point to using a procedure pointer component. – IanH Jul 27 '15 at 11:52
  • I have updated the answer accordingly. Does this mean that the lifetime of the internal function is essentially the same as local "automatic" variables (roughly speaking)? – roygvib Jul 27 '15 at 16:50
  • @roygvib Yes, the host function must not exit, it must still be on the stack. There are no lexical closures in Fortran (or C and similar). – Vladimir F Героям слава Jul 27 '15 at 18:14
  • @VladimirF Thanks very much. Yes, I was interested something like that (passing a nested or local function + its environment as a "package"), but seems invalid in this case. – roygvib Jul 29 '15 at 12:10
  • @roygvib, your first solution works perfectly. I just realized that using a nested function is precisely how I dealt with the same problem in Python; passing self.integrand doesn't work there either! Thanks! – dbrane Jul 29 '15 at 20:38