7

I have a Fortran 90 subroutine which takes a function as an argument, and I would like to pass a modified version of that function into another subroutine. I want the program to look something like this:

subroutine foo(f, ...)
    real                 :: pt(2), dir(2)

    interface
    function f(x) result(y)
        real, intent(in) :: x(2)
        real             :: y
    end function f
    end interface

    pt = ...
    dir = ...
!! Somehow create g(x) = f(pt + x*dir)
    call bar(g)

end subroutine foo

subroutine bar(g)
    interface
    function g(x) result(y)
        real, intent(in) :: x
        real             :: y
    end function g
    end interface

!! Do stuff with g
end subroutine bar

I've managed to do something similar when 'g' only needs to use normal variables, not a function. In that case I made it a global function, using global variables, and assigned to those global variables in 'foo'. However, I can't find a way to turn 'f' global, or assign it to a global function.

Anybody have any ideas how to do this? The solution can be as hacky as you want.

Eric Langlois
  • 572
  • 4
  • 12

2 Answers2

7

This is not so easy. In some languages you can pass pointers to nested functions in a so called closure. This is not possible in Fortran (or C and similar languages), because the data are destroyed with the stack of the higher function. I would suggest you to try function objects, i.e. a class with a function pointer (or more) and data needed for the function. In this way you can even do function composition and similar functional stuff.

More on the concept http://en.wikipedia.org/wiki/Function_object

Below is a sample for a function object for composition of two single argument functions:

module ComposeObj
  use Parameters, only: rp
  use AritmFunctions, only: fce
  implicit none

  private
  public Compose

  type Compose
    private
    procedure(fce),pointer,nopass :: f1 => null(),f2=>null()
  contains
    procedure,public :: call => helper
  end type Compose

  interface Compose
    procedure NewCompose
  end interface

 contains

  function NewCompose(f,g)
    procedure(fce) :: f,g
    type(Compose) :: NewCompose

    NewCompose%f1 => f
    NewCompose%f2 => g
  end function NewCompose

  pure real(rp) function helper(this,x)
    class(Compose),intent(in) :: this
    real(rp),intent(in) :: x
    helper = this%f1(this%f2(x))
  end function helper

end module ComposeObj
  • Thanks for the example, but would it work in Fortran 90? A lot of the keywords you used are new to me; in particular I've only be able to find references to 'procedure' in 2003 not 90. – Eric Langlois Mar 14 '12 at 19:06
  • No, procedure pointers are a feature of Fortran 2003. Fortran 90 is completely obsolete and the earliest standard one should use, if very being conservative, is Fortran 95, which corrected several problems of Fortran 95. Fortran 2003 and 2008 are required for modern Fortran programming. – Vladimir F Героям слава Jul 30 '20 at 11:38
2

You could do a lot with procedure pointers, constructing a function that is a combination of other functions. See Function pointer arrays in Fortran for a code example.

Community
  • 1
  • 1
M. S. B.
  • 28,968
  • 2
  • 46
  • 73