Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
This community is designed for sharing of public information. Please do not share Intel or third-party confidential information here.
27153 Discussions

Can a subroutine "exit" a loop of its calling procedure?

machalot
Novice
439 Views

I have program that loops indefinitely until a stop condition is reached.  In a flat code structure, you can use the "exit" command to stop the loop.  However, I need to call a subroutine to check the stop condition.  If the stop check decides to keep going, I need to do some more calculations to prepare for the next iteration.  I would like to avoid doing these calculations unnecessarily when the loop is stopping.

Is there a direct way to cause the stop check to stop the loop at the level of the calling program, sort of like an "exit" command that triggers an "exit" above the scope of the subroutine?  Here's an example of the structure of my code.

 

 

 

 

program jump_out
implicit none
real :: x = 0.0, y = 0.0, z
logical :: keep_going = .true.

do while (keep_going)
   z = some_math(x,y)
   call stop_check(z,keep_going)
   call prepare_next_iteration(x,y,z)
end do while

call end_of_run()

contains
    subroutine stop_check(z, keep_going)
       real, intent(in) :: z
       logical, intent(out) :: keep_going
       
       if(z > 0.0) then
         keep_going = .false.
         ! HERE I WOULD LIKE TO EXIT THE MAIN LOOP
       else
         keep_going = .true.
       end if
    end subroutine stop_check

end program jump_out

 

 

 

I realize there is a rather trivial solution given this example code, to test the value of "keep_going" before running "prepare_next_iteration" and exit if false.  But my actual application is several layers deep, and that would be invasive because it requires inserting many levels of tests.  I am hoping for a simpler solution.

 

 

 

 

0 Kudos
1 Solution
Steve_Lionel
Black Belt Retired Employee
418 Views

There isn't that feature, but instead your stop_check should be a function that returns a LOGICAL value, tested in the loop, rather than a subroutine argument. The caller can then do what is needed if the function indicates that it should stop.

There is ERROR STOP, of course, but that's a "big hammer". This could also be a good use for exceptions, which we want to add to the language, but there is no agreement of what they should look like given that we don't want code to slow down just because exceptions exist.

View solution in original post

7 Replies
Steve_Lionel
Black Belt Retired Employee
419 Views

There isn't that feature, but instead your stop_check should be a function that returns a LOGICAL value, tested in the loop, rather than a subroutine argument. The caller can then do what is needed if the function indicates that it should stop.

There is ERROR STOP, of course, but that's a "big hammer". This could also be a good use for exceptions, which we want to add to the language, but there is no agreement of what they should look like given that we don't want code to slow down just because exceptions exist.

machalot
Novice
412 Views

Thanks for the fast reply!

I was hoping to avoid the method you're suggesting because (even after making stop_check a function) in my actual code it will require passing the keep_going result up through several levels, testing it at each level to bypass the subsequent calculations at that level.  With the state of this legacy code, that will not be clean.  But we probably just have to do it.

(Also, this is for normal program termination, so I don't think an exception would be the right tool here, would it?)

Steve_Lionel
Black Belt Retired Employee
404 Views

In languages with structured exceptions, this is a typical way of handling an "out-of-band exit" situation. Ideally you want each procedure level above you the chance to handle the exception and do any cleanup or other processing it needs. I fondly remember OpenVMS, which had structured exception handling built in to the OS, available to all languages.

If you don't want to make it a function, you could use a module variable that gets checked where needed, but that's not as elegant.

Cover your eyes, but I am also going to mention the deleted feature "alternate return". Don't use it.

FortranFan
Honored Contributor II
375 Views
@Steve_Lionel wrote:
.. Cover your eyes, but I am also going to mention the deleted feature "alternate return". Don't use it.

 

"Alternate return" is obsolescent and not yet deleted from the Fortran standard.  It can offer a solution close to or exactly what OP seeks but OP will be better off avoiding it.  There was a similar discussion at the Fortran Discourse site not too long ago and here's the dreaded approach with "alternate return":

DavidWhite
Black Belt
387 Views

If you give your DO loops names, eg.

LOOP1: DO

    LOOP2: DO

....

   END DO LOOP2

END DO LOOP1

 

Then you can put an exit statement EXIT LOOP1 inside the LOOP2 DO group (or even deeper if you need).

mecej4
Black Belt
349 Views

It is easy to wish for something that solves an immediate problem, only to find after a lengthy study that it is not reasonable to do so.

The caller does not know that keep_going may be set in the subroutine. It could be an input variable.

The called  subroutine may have lots of arguments that are set, but it does not know which ones may be used to trigger actions in the caller. Nor does it know if the action is in the immediate caller or another routine farther away in the call chain.

Furthermore, when the caller and the subroutine are separately compiled, as they may be if they are external routines in separate files, within a subroutine nothing is known about the variables in any other subroutine, unless there is module/common association.

The difficulty with non-local GOTO, exceptions, etc., is that a well-established protocol is needed. When should an exception be checked? What action is needed when it is detected? Where should that action be performed -- in the callee, the caller, an intermediate location, or the RTL?

Can all this coordination be achieved with very little performance penalty? What about error detection? Suppose a subroutine is expected to set a condition, but the programmer forgot to implement that. How is that error going to be detected?

 

jimdempseyatthecove
Black Belt
338 Views
logical :: keep_going ! *** do not initialize here
...
keep_going = .true.
do while(keep_going)
  ... ! some inline code
  if(formerly_called_subroutine([args])) exit ! non-recoverable error for loop
  ...
  if(formerly_called_subroutineTwo([args])) then
    ... ! adjust parameters
    cycle
  end if
  ...
  if(formerly_called_subroutineThree([args])) then
    keep_going = .false.
    ... ! cleanup
  end if
  ... ! necessary work regardless of error
end do
...

Jim Dempsey

Reply