fortran access routine inside "contains" statement in separate routine

Viewed 58

I am trying to reset the save variable inside a routine, a way I theorize going around this is having a reset() function that zeros out the data inside, however I cannot directly call this function as it is inside the "contains" keyword, is there a way to override this protection? I've looked into "interface" and "external" keywords but the documentation for this particular question seems sparce

program main
   implicit none

   call count ! 1
   call count ! 2
   call count ! 3

   ! cannot make a call to reset
   ! is there any way to remove this
   ! protection and access this directly?
   call reset
   call count ! 1 etc...
end program main

subroutine count
   integer, save :: numcalled = 0

   numcalled = numcalled + 1
   print *, numcalled

   return
contains

   subroutine reset
      numcalled = 0
   end subroutine reset
end subroutine count

This is a small example to get the idea of what I aim to do to a project with a bunch of "save" variables and I need to reset these as this code will be called several times and stuff needs to be fresh for each function call (no static variables floating around with bad state), if I can override this protection to access subroutines inside a "contains" keyword this would be a very minimally invasive task

3 Answers

The procedure declared after contains is an internal procedure. It cannot be called from the outside world. There is no way.

You would have to store the data in a module and have a reset module procedure

module subs
    implicit none

    integer, save :: numcalled = 0

contains

  subroutine count
   

    numcalled = numcalled + 1
    print *, numcalled

  end subroutine count

  subroutine reset
      numcalled = 0
  end subroutine reset
end module

or have a special argument to your count subroutine to tell it it should call its reset function for you.

subroutine count(do_reset)
   integer, save :: numcalled = 0
   logical :: do_reset

   if (do_reset) call reset

   numcalled = numcalled + 1
   print *, numcalled

   return
contains

   subroutine reset
      numcalled = 0
   end subroutine reset
end subroutine count

You can make the argument optional. You can also return from the subroutine after calling reset without executing the rest of the code.

Anyway, no matter what solution you choose, all procedures should be in modules if there is no specific reason against.

The program you present is a great example of how giving functions state can lead to problems. When you just need state to persist forever on a single thread, functions with state are okay, but if you need to modify / reset the state, or if you are using multiple threads or multiple "instances" of the function, saved variables are usually too inflexible.

A flexible alternative would be to use a Counter class, which exposes your count function, and also has a reset function. This allows you to modify the state as desired, and also to have multiple instances of the class in multiple parts of your code without having to worry about shared state.

This would look something like

module counter_mod
  implicit none

  type :: Counter
    integer, private :: counter_ = 0
    procedure, public :: count
    procedure, public :: reset
  end class
contains
  subroutine count(this)
    class(Counter), intent(inout) :: this
    this%counter_ = this%counter_ + 1
    print *, this%counter_
  end subroutine
  
  subroutine reset(this)
    class(Counter), intent(inout) :: this
    this%counter_ = 0
    print *, this%counter_
  end subroutine
end module

and then your program would look like

program main
  use counter_mod
  implicit none
   
  Counter my_counter

  call my_counter%count() ! 1
  call my_counter%count() ! 2
  call my_counter%count() ! 3
  call my_counter%reset() ! 0
  call my_counter%count() ! 1 etc...
  
  ! And then you can have another counter.
  Counter another_counter
  
  call another_counter%count() ! 1
  call my_counter%count() ! 2
  call my_counter%reset() ! 0
  call another_counter%count() ! 2
end program main

Insofar as you are changing the state of something (a counter) then an object-oriented approach seems more natural.

Either way, to get to a separate routine you would need modules, as already pointed out.

One nice way to set or reset the variable if required might be to use an optional argument, as in the routine below. If so, then you still need an explicit interface, for which a module is still the easiest standard way of providing it.

BTW, if you use the variable name COUNT then I think you are probably hiding a standard library routine.

Code to use an optional argument to set or reset the counter to whatever you like:

module mods
   implicit none

contains

   subroutine counter( startValue )
      integer, optional :: startValue
      integer, save :: numcalled = 0

      if ( present( startValue ) ) then
         numcalled = startValue
      else
         numcalled = numcalled + 1
         print *, numcalled
      end if

   end subroutine counter
end module mods

!=================================================

program main
   use mods, only: counter
   implicit none

   call counter ! 1
   call counter ! 2
   call counter ! 3

   call counter( 0 )
   call counter ! 1
   call counter ! 2
   call counter ! 3

   call counter( 100 )
   call counter ! 101
   call counter ! 102
   call counter ! 103

end program main
       1
       2
       3
       1
       2
       3
     101
     102
     103
Related