2

My scenario: I would like to have my Fortran (>=95) program choose one of two subroutines in a calculation, based on a parameter. As an example, let's have two subroutines, foo, which subtracts; and bar, which adds its two integer arguments. I furthermore have a subroutine callingsub which gets either foo or bar as an argument. Full program could look like

program choice

implicit none

integer :: a,b

a=3
b=4

call callingsub(a,b,foo)

contains

  subroutine foo(a,b,c)
    integer, intent(in) :: a,b
    integer, intent(out) :: c

    c=a-b
  end subroutine foo

  subroutine bar(a,b,c)
    integer, intent(in) :: a,b
    integer, intent(out) :: c

    c=a+b
  end subroutine bar

  subroutine callingsub(a,b,sub)
    integer, intent(in) :: a,b

    interface 
       subroutine sub(a,b,c)
         integer, intent(in) :: a,b
         integer, intent(out) :: c
       end subroutine sub
    end interface

    integer :: c

    call sub(a,b,c)
    write(*,*) 'Your answer is ',c
  end subroutine callingsub

end program choice

Now to switch between foo and bar I have to recompile, but I would rather have a choice at run time. I imagine having an integer flag, which, if 0 chooses foo and if 1 chooses bar. I could of course write a subroutine

subroutine baz(a,b,c,flag)
  integer, intent(in) :: a,b
  integer, intent(out) :: c
  integer, intent(in) :: flag

  if (flag==0) then
    c=a-b
  else if (flag==1) then
    c=a+b
  else
    write(0,*) 'illegal flag ', flag
    stop 1
  end if
end subroutine baz

which uses the flag to decide, however, the call to callingsub will be in a huge loop, and my feeling tells me, that it would be better to have the decision on foo or bar before the loop.

Is there any possibility to have a conditional to decide in the main program? I imagine something like

if (flag==0) then
  chosensub=foo
elseif (flag==1) then
  chosensub=bar
else
  !error and exit
end if

and then call callingsub(a,b,chosensub), which unfortunately does not work. I cannot put interfaces into a conditional either.

I appreciate any help on this, and hope I made myself clear enough!

PS I have access to Intel ifort 18.0.5 20180823, so I am not limited to F95.

cauchy42
  • 191
  • 2
  • 9
  • Take a look at https://stackoverflow.com/questions/13616416/calling-a-subroutine-multiple-times-with-different-function-as-argument-each-tim – Ian Bush Dec 12 '18 at 10:46
  • These were two quick and very helpful comments, the thought of pointers of any kind didn't even cross my mind, let alone the possibility of pointing to methods... – cauchy42 Dec 12 '18 at 11:00

1 Answers1

4

OK, for future reference here is what I did, after following @M.S.B s answer here, so thanks @HighPerformanceMark and @IanBush for pointing (haha) in that direction:

program choice

implicit none

integer :: a,b,flag

interface
   subroutine chosensub(a,b,c)
     integer, intent(in) :: a,b
     integer, intent(out) :: c
   end subroutine chosensub
end interface

procedure(chosensub), pointer :: subptr=>null()

read(*,*) flag

if (flag==0) then
   subptr=>foo
else if (flag==1) then
   subptr=>bar
else
   write(0,*) 'error message'
   stop 1
end if

a=3
b=4

call callingsub(a,b,subptr)

contains

! as in OP

end program choice
cauchy42
  • 191
  • 2
  • 9
  • 1
    If execution performance is your goal, did you time the difference between your original approach (branch on the basis of an integer flag) and this pointer approach? There are other reasons for why you might prefer to use a procedure pointer, but you may find it executes slower. With the integer flag approach, the prospective instruction streams are more visible to the compiler, with the pointer approach the compiler has to be clever enough to connect the pointer assignment with the later invocation. Results will be very compiler and problem dependent. – IanH Dec 12 '18 at 21:19