- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@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":
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page