How to pass subroutine names as arguments in Fortran?
Asked Answered
T

2

7

What is the syntax for passing subroutine names as arguments? Schematically:

  .
  .
call action ( mySubX ( argA, argB ) )
  .
  .

subroutine action ( whichSub ( argA, argB ) )
  ...
call subroutine whichSub ( argA, argB )
  ...
end subroutine action

The goal is to have call subroutine whichSub ( argA, argB ) act as call subroutine mySubX ( argA, argB ). My preference is to avoid avoid passing a switch parameter and then use SELECT CASE.

Tilsit answered 27/9, 2015 at 15:46 Comment(1)
Which flavor and version of Fortran?Ethical
J
17

It is

call action(mySubX)

provided action looks as

subroutine action(sub)
  !either - not recommmended, it is old FORTRAN77 style
  external sub
  !or - recommended
  interface
    subroutine sub(aA, aB)
      integer,intent(...) :: aA, aB
    end subroutine
  end interface
  ! NOT BOTH!!

  call sub(argA, argB)

provided action knows what to put there as argA, argB to represent aA, aB.

Otherwise, if you want to pass also the arguments

call action(mySubX, argA, argB)

subroutine action(sub, argA, argB)
  !either - not recommmended, it is old FORTRAN77 style
  external sub
  !or - recommended
  interface
    subroutine sub(aA, aB)
      integer,intent(...) :: aA, aB
    end subroutine
  end interface

  integer, intent(...) :: argA, argB

  call sub(argA, argB)

I don't think it is good to use function pointers here, they are good when you have to change the value of the pointer (the subroutine it points to) sometimes. Normal procedure arguments worked in FORTRAN77 and continue to work even now.


So as requested in the comment, if you are in a module and procedure with the right interface is accessible from the module (perhaps in the same module), you can use the procedure statement to get rod of the interface block:

module subs_mod
contains
  subroutine example_sub(aA, aB)
    integer,intent(...) :: aA, aB
    !the real example code
  end subroutine
end module

module action_mod
contains

  subroutine action(sub)
    use subs_mod
    procedure(example_sub) :: sub

    call sub(argA, argB)
  end subroutine
end module

but more likely, instead of a real subroutine you will create an abstract interface which you would reference with the procedure statement so in the end everything will be similar as before:

module action_mod

  abstract interface
    subroutine sub_interface(aA, aB)
      integer,intent(...) :: aA, aB
    end subroutine
  end interface

contains

  subroutine action(sub)
    procedure(sub_interface) :: sub

    call sub(argA, argB)
  end subroutine
end module
Julienne answered 27/9, 2015 at 18:10 Comment(4)
Great explanation. Thanks for the tips on modern use of the language and function pointers. Can we use modules to obviate the INTERFACE?Tilsit
If some compatible procedure is accessible from some module, you can use procedure(mySubX) :: sub. You can also have an abstract interface for it in the module and use the same procedure(nameOfTheInterface).Gemmagemmate
Maybe I'm mistaken, but I had the impression that the external keyword is only required in the caller (the function that calls with a procedure parameter), not the callee. Otherwise, per 12.4.2 (Fortran 2008) it has an implicit interface. In 5.3.9#2: "If an external procedure or dummy procedure is used as an actual argument or is the target of a procedure pointer assignment, it shall be declared to have the EXTERNAL attribute." It's only mandatory in the callee under special circumstances (basically that the interface makes use of F90+ features).Morion
However, I agree that it's much better to use abstract interfaces and modules in such a case.Morion
G
2

I think use module to avoid interface is a good modern Fortran practice because it gives a cleaner interface.

Here is the ideal the realize that:

module part:

module foo
contains

subroutine callsub(sub,arg1,arg2)
!This subroutine is used to call other subroutines
external::sub !Use external to tell compiler this is indeed a subroutine
call sub(arg1,arg2)
end subroutine

subroutine sub(arg1,arg2)
!The subroutine to be called.
!do something
end sub

end module

Then here is the main program:

program main
use foo !Use module automatically avoids using interface.
implicit none
!Declare about args
call callsub(sub,arg1,arg2)
end program

Here is my demonstration to show exactly how this can be realized.

Grouch answered 21/2, 2017 at 0:56 Comment(3)
Thanks for correcting me. I'm a newbie here and I'm still learning the manners. I added more content that is more specific related to the question.Grouch
Nice work, this is a much better answer, and Welcome to Stackoverflow!Potash
Well, this design does remove the need for the interface block, but the point of subroutine passing is that you can pass any subroutine. Now you have explicit interface to sub in the main program, but not in callsub. It would be much better modern Fortran to have procedure(sub) there instead of the external. And then you can just have an abstract interface in the module instead of the whole sub. And then you realize that you are again at an interface block...Gemmagemmate

© 2022 - 2024 — McMap. All rights reserved.