Jumping back to a different Tcl stack frame

Go To StackoverFlow.com

3

Is there an easy way to unroll the stack in Tcl? I have this strange problem where I have to get back to a particular stack frame, literally. I can get all the frame information using info command, but to actually get to a particular frame I will have to set some local variables in the each procedure and check them accordingly. I was wondering if there is an easier way.

2012-04-04 23:40
by ilya1725
For debugging or... - NoName 2012-04-04 23:43
For running. I need to get from one procedure to a different one at some other level - ilya1725 2012-04-04 23:49
While I think that the Donal Fellows' answer is perfect, the subject should better be rephrased since "unrolling the stack" is a term deeply connected to exception handling/debugging in the minds of most programmers. Any idea on how to rephrase sensibly - kostix 2012-04-05 14:18
Agree. Will 'moving in the stack' be better? Maybe 'moving from one stack frame to another' - ilya1725 2012-04-05 18:28
I've had another go at improving the title; that returning works for you indicates what you're after. True stack unrolling though… the only real answer to that is either uplevel with an integer argument larger than 1, or switching to 8.6 (which is actually a stackless Tcl implementation under the covers, a colossal change that it's taken quite a long time to bed in properly) - Donal Fellows 2012-04-06 05:34


4

If you need to get code to do a non-local return (i.e., skipping back several levels) you can do this from 8.5 onwards with the -level option to return. See this example:

proc foo-a {} {
    puts a-in
    foo-b
    puts a-out
}
proc foo-b {} {
    puts b-in
    foo-c
    puts b-out
}
proc foo-c {} {
    puts c-in
    foo-d
    puts c-out
}
proc foo-d {} {
    puts d-in
    bar
    puts d-out
}
proc bar {} {
    puts bar-in
    return -level 3
    puts bar-out
}
foo-a

This works by throwing a special kind of exception; the details are quite hidden. Alternatively, you can also use try and throw if you've got 8.6 or a scripted implementation of them (see the Tcler's Wiki for Tcl code that was used during the discussion of the code in 8.6).

With older Tcl versions, the simplest mechanism is to use return -code 42 and to put some code in place up-stack to catch the custom exception and determine if it is the magic value (42 here; it'll be the result of the catch) and respond appropriately. This can be quite effective, but is also messy. Which is why 8.5 onwards give you tools that are easier to use.

2012-04-05 10:40
by Donal Fellows


3

Short answer is that you'd probably be better of rethinking your design.

Longer answer is that the only real way (I can think of) to do this is to throw an error and catch it back up at the level you need to stop it at. Other than, of course, the less flame-worthy way of checking variables all the way up the call stack. Using errors for control flow, however, is... bad form.

2012-04-05 00:26
by RHSeeger


1

If you're willing to be bleeding-edge, coroutines (available in 8.6) might meet your needs.

Otherwise, what you might try is to note the level of the routine you need to get to when you're there (i.e., set a global variable to [info level]), and then in the deeper proc, [uplevel] to that absolute stack frame. Something like

proc need-to-be-here {} {
   set ::myframe [info level]
   deeper-calls
}

proc deepest-call {} {
   uplevel #$::myframe {what I need to do}
}

(untested)

2012-04-05 00:34
by evil otto
+1 for evil use of uplevel. Just don't use it like that in any code I have to maintain! :- - Donal Fellows 2012-04-06 06:12
Ads