0

I am working in a TCL automation environent at my workplace. I'm trying to run a delayed command in my script using "after". The problem i encounter is that when i try to specify commands with variables inside a code block under an "after" , the vars are not recognized and i get an error message. I'll quote the relevant parts of the code.

proc test_script1 {{bh0 0} ...more vars....} {
.
.
.
after [expr 20000]   {
   set some_value[ ns1::probe_TCP_connections $bh0 $main_duration 1  15]    }

puts "the value i got is $some_value"
}

And i seem to get an error: can't read "some_value": no such variable

Can anyone suggets what is the problem , and how to oversome it? thx

4
  • Did you read the manual? The way you wrote the command makes it so that the command for after is made to run at the designated delay while continuing with the script. So puts "the value i got is $some_value" is being executed before the variable is set. Additionally, your set inside the after block looks suspicious. Commented Mar 25, 2015 at 9:47
  • you are right about the puts being executed right after.I'll clarify , lets say i add a delay before the puts , aith another "after". the interpreter still doesn't recognize the variable Commented Mar 25, 2015 at 10:05
  • Also, in the manual: "The command will be executed at global level (outside the context of any Tcl procedure)" so you will have to fetch the variable from the global level, and there's quite a few ways to do that. Commented Mar 25, 2015 at 10:50
  • First of all thanks. I'll be thankful if you could elaborate on that. Commented Mar 25, 2015 at 10:56

2 Answers 2

3

You can capture the values of local variables with the help of apply (and it's advisable to use list to build callbacks when they get even slightly non-trivial). Since the body of an apply is a lambda expression with its own scope — a little nameless procedure — you have to use global in it to access state that will persist, and in any case after callbacks are called from the global namespace always (since the mechanism doesn't know how to keep arbitrary stack frames around for the duration of the asynchronous operation).

after 20000 [list apply {{bh0 main_duration} {
    global some_value
    set some_value [ns1::probe_TCP_connections $bh0 $main_duration 1 15]
}} $bh0 $main_duration]

global some_value
vwait some_value
puts "The value was changed to $some_value"

It's possible to get even more sophisticated, especially in Tcl 8.6 which has a coroutine system that can be used to hide the complexity of using continuation passing style programming.

Sign up to request clarification or add additional context in comments.

Comments

1

Here is a proc that will do something similar to what you're trying to do:

proc foo {} {
    # Make 'a' available to both the global scope and local scope
    global a
    # This is to check the current level
    puts "Current level: [info level]"
    # The command to be run in 500 ms, and I have added another check for the level
    after 500 {puts "Current level: [info level]"; set a 100}
    # Wait for further execution until the global variable 'a' changes
    vwait a
    # Prints a
    puts "Value of \$a: $a"
}

The output of the above will be:

Current level: 1
# After 500 ms, the below will print
Current level: 0
Value of $a: 100

When you use after, the variable a is being created in the global scope, that is not accessible to the proc unless explicitly given access. One of the simplest ways is to first make sure that a within the proc is accessible both globally and locally, with global (level 0).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.