Need info about return -<options> in TCL - tcl

What are the options are available in TCL return command ? i could see -code -errorcode -errorinfo as options for return command.. but couldn't understand the difference between them.
proc new {} {
set name george
puts "Hello $name"
return -code ok
}
puts [new]
Here i didn't get any output, it is like normal return command. But could get more option for -code in return command like break , continue, error and also retun... What is the use of this options.
And also below code makes me confuse ..
proc new {} {
set name george
puts "Hello $name"
return -code return -code return -code return
}
like that we can give more , it didn't get errored , what is the use of this option..

The options for return are listed on the man page: return.
-errorcode, -errorinfo, -errorstack, and -level are used to describe aspects of an exception: -options allows one to pass a dictionary of exception data to the command. Neither of these is likely to be useful to you until you have a thorough understanding of the language and are writing advanced exception handling code.
The -code option is used to specify what kind of handling surrounding code will need to perform. ok means that no special handling is necessary and that the result of the command is valid data; error means that a catch or try handler needs to be in place or the program will end, and also that the result of the command is an error message. break and continue need an enclosing looping command (or a catch / try command with an appropriate handler clause). return makes the command where it is used behave like a return command. You probably won't need to deal with this option unless you are writing your own control structures.
The -level option also lets the return command return to somewhere else than in the caller (but still in the call stack). If the value is 0, the return action is "in place": return will return from itself without terminating the script it is part of. The first line of
set foo [return -level 0 bar]
puts {I'm still alive}
will be executed as set foo bar, and evaluation will continue with the puts command. -level 1 means return to the caller (the usual behavior), -level 2 means return to the caller's caller, and so on.
The return command accepts just about anything in its argument list, and like many commands won't complain about arguments being repeated. return -code return -code return -code return does the same thing as return -code return.

Related

How to control the character length of strings in tcl errors?

Long strings which appear in tcl error messages are elided with ... after 150 characters:
proc helloWorld {a} {
throw
}
helloWorld "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
Error:
invalid command name "throw"
while executing "throw "
(procedure "helloWorld" line 2)
invoked from within "helloWorld "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff..."
(file "mytcl" line 6)
Is there a way to change this so that more characters are shown?
In my large commercial tcl application, customers use file paths that are more than 150 characters long. We see 100 characters for a repository name, plus 100 characters for a location within the repository. Since only 150 characters appear, this means the "useful" part of the filename is not displayed in error messages. We have suggested to the customer that they could shorten the path using a symlink, but they do not accept this.
You can install your own background error handler.
You can log the error, display it how you want, etc.
interp bgerror {} ::bgerrhandler
# give err and res whatever names that make sense to you
proc ::bgerrhandler { err res } {
# do stuff
return
}
The problem that you've got is that the line-shortening is applied during the construction of the error trace message. It's already long gone by the time you get to see it. What you need instead is to install a custom background error handler using the new API that picks critical things out of the error stack. (The old API using a simple bgerror command is unsuitable for this task as it hides the result option dictionary for backward compatibility reasons.)
proc MyErrorHandler {msg opts} {
if {[dict get $opts -code] == 1} {
# Actually an error!
puts "ERROR: $msg"
foreach {op details} [dict get $opts -errorstack] {
puts stderr "$op :> $details"
}
return
}
# Transfer to standard implementation; tailcall recommended for this from 8.6 on
::tcl::Bgerror $msg $opts
}
interp bgerror {} MyErrorHandler
Here's an example of this working:
# Error generation code
proc foo {x} {throw BOOM $x}
proc bar {y} {foo $y$y$y$y}
proc grill {} {bar skdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsd}
# Now actually do things in the background
after idle grill; after 50 set z 1; vwait z
That will print this message:
ERROR: skdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsd
INNER :> returnImm skdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsd {-errorcode BOOM}
CALL :> foo skdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsdskdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsd
CALL :> bar skdfhglsdjklfgjhsdlfgjksdlkfhgklsdhfjklghskldfsldfkjghlsd
CALL :> grill
The INNER indicates what bytecode opcode actually threw, and then there's a sequence of CALLs to give the exact arguments lists that lead to that point.

Nested function calls in CMake (use one's result as another's argument)

Test CMakeLists.txt:
function(foo param)
return("hello")
endfunction()
message(foo("oops"))
When "cmake ." is run with the above file, it prints "foo(oops)". I expected it to print "hello".
How can I make one function receive the result of another in CMake?
CMake functions don't "return values" in the way the code example you gave thinks they do. In general, if a CMake builtin or function wants to return a value, it needs to take the name of a variable to store the result in:
function(foo param ret)
set(${ret} "hello ${param}" PARENT_SCOPE)
endfunction()
Then, you need to construct the control flow in the caller yourself:
foo("world" hello_world)
message(STATUS "foo returned: ${hello_world}")
foo(${hello_world} hello2)
message(STATUS "foo second call returned: ${hello2}")

Why doesn't tcl's "if" return values?

Why doesn't this work like I would think?
set x [if {1} {return 2} {return 3}]
The command output shows 2, but when you check the value of x, it is found to be undefined..!
I have read a workaround, ...
proc give {x} {return $x}
set x [if {1} {give 2} {give 3}]
...but I don't understand why the first one doesn't work.
From the tcl 8.5 documentation for if:
"The return value from the command is the result of the body script that was executed, or an empty string if none of the expressions was non-zero and there was no bodyN."
Questions:
Why doesn't set x [if {1} {return 2} {return 3}] set a value for x?
Given that set x [if {1} {return 2} {return 3}] doesn't work, how does tclsh display "2" on the interactive shell?
Given that tclsh displays "2" on the interactive shell, how is it that set x remains undefined?
See http://www.tcl.tk/man/tcl8.6/TclCmd/return.htm
Return codes are used in Tcl to control program flow. A Tcl script is a sequence of Tcl commands. So long as each command evaluation returns a return code of TCL_OK, evaluation will continue to the next command in the script. Any exceptional return code (non-TCL_OK) returned by a command evaluation causes the flow on to the next command to be interrupted. Script evaluation ceases, and the exceptional return code from the command becomes the return code of the full script evaluation. This is the mechanism by which errors during script evaluation cause an interruption and unwinding of the call stack. It is also the mechanism by which commands like break, continue, and return cause script evaluation to terminate without evaluating all commands in sequence.
So the evaluation of the script is aborted, x does not get set, and 2 is returned and printed as a result.
The expr command returns a value, so
set x [if 1 {expr 2} {expr 3}]
But if you're doing that, you might as well use the ternary operator
set x [expr {1 ? 2 : 3}]
The reason that set x [if {1} {return 2} {return 3}] doesn't work the way you expect, is, as already pointed out, the fact that return is a command that causes script evaluation to terminate. The "workaround", using a give procedure, demonstrates the fact that by default, the return command terminates 1 level of the call stack.
However, the "workaround" really is a pointless exercise in masochism: The return command allows you to specify where to return to, using the -level option introduced in Tcl 8.5. Try this, see how it works for you.
set x [if {1} {return -level 0 2} {return -level 0 3}]

Can I catch "An unhandled win32 exception" caused by an executable I'm calling from a TCL script?

catch { exec asdf.exe args }
The executable whose name I am passing to the exec command causes an unhandled exception.
catch { exec ...} seems to only catch non-zero return codes, not exceptions.
What's the proper way to handle this?
Note I've no access to the source code of the executable I am calling.
As pointed out by Hans Passant and schlenk, you cannot catch an exception from an external application.
As you have already observed, catch { cmd } will only inspect return codes.
What you can do with catch, however, is capture all the output in a variable by doing:
catch {exec asdf.exe args} err_msg
This will set the output from the command as variable $err_msg.
catch also conveniently returns a 1 iff the command returns a non-zero code, allowing you to do the following:
if {[catch {exec asdf.exe args} err_msg]} {
# inspect your output in $err_msg here;
} else {
# successful execution aftermath here;
}
[NB: Incidentally, should the command return a 0, $err_msg still contains the output from the command.]
Assuming your external application prints something sensible on exception, you should be able to inspect what gets printed and react accordingly.

evaluating an expression to true in a if statement with Tcl

I'm having serious biggies trying to figure out how to evaluate an expression in a If statement using Tcl. This is what I have:
#!/bin/sh
#The next line executes wish - wherever it is \
exec wish "$0" "$#"
set apid "/var/run/apache2.pid"
set apsta [file exist $apid]
if { $apsta == 1 }
{
set result ":)"
}
else
{
set result ":("
}
label .status -text $result
pack .status
and this is what I get from the terminal:
# wan27
Error in startup script: wrong # args: no script following " $apsta == 1 " argument
while executing
"if { $apsta == 1 }"
(file "/usr/bin/wan27" line 9)
I'm just trying to output a happy smiley if Apache is running, a sad one if Apache is stopped - based upon the boolean condition whether or not the file "/var/run/apache2.pid" exists ...
The syntax of a Tcl if statement is
if condition statement else statement
which must be on one line. Braces allow continuation across lines and their placement is mandatory:
if { $apsta == 1 } {
set result ":)"
} else {
set result ":("
}
As you have it written, Tcl sees
if { $apsta == 1 }
and stops there yielding a syntax error.
MSW have already given you the correct answer but I think a little bit more explanation is needed to clear up some other confusions you are having based on your comments.
I will first explain things using non-tcl terminology since I think it is less confusing that way.
In tcl, if is not a statement. if is a function. That is the reason why the opening brace need to be on the same line: because a newline terminates the list of arguments to a function. For example, in the following code:
a b c d
e f
the Tcl interpreter will see two function calls. The first to function a with arguments b, c and d and the second to function e with a single argumrnt f. Similarly, in the following:
if a
b
Tcl sees a call to the function if with a single argument. Since if expects at least two arguments it (not the interpreter itself) throws an error complaining about wrong number of arguments.
This also explains why there must be a space between if and its first argument. It's just because in tcl names of variables and functions are literally allowed to contain almost anything including spaces, commas and non-printing characters like NUL. For example, you can define a function called a{b}:
proc a{b} {} {puts HA!}
a{b} ;# <-- prints out HA!
So if you do something like:
if{x} {y}
tcl will complain that the function if{x} is not defined.
if is not the only thing that works like this. Tcl doesn't really have keywords, just a standard library of built-in functions like for, foreach and while. The same rules apply to all of them.
not really important:
On a side, the if function in tcl works like the ternary operator in C: it returns a value. In fact you can do the following:
# It is unfortunate that this "give" function is not built in.
# We use it to return something from if without actually
# calling return:
proc give {x} {return $x}
set something [if {$x} {give "Very true indeed."} {give "Unfortunately false."}]