Function Overloading in TCL - tcl

Are there any packages or any specific way to support function or procedure overloading in TCL??
This is my scenario. I need to write a generic procedure that accepts two or 3 files, wherein I may or may not have the third file (File3)
proc fun { File1 File2 File3 }
{
}
proc fun { File1 File2 }
{
}

There is no overriding in tcl. The second declaration will just replace the first one.
But you handle it with a single procedure. There are two ways at least:
1) Specify the last argument with its default value. Then it will be optional when you calls the function.
proc fun { file1 file2 {file3 ""} } {
if {$file3 != ""} {
# the fun was called with 3rd argument
}
}
2) Use the special argument args, which will contain all arguments as a list. And then analyze the number of arguments actually passed to.
proc fun { args } {
if {[llength $args] == 3} {
# the fun was called with 3rd argument
}
}

Tcl doesn't really support procedure overloading, which makes sense when you consider that it doesn't really have types, per se. Everything is a string that can, depending on value, be interpreted as other types (int, list, etc).
If you can describe what it is you're trying to accomplish (why you think you need overloading), we might be able to make a recommendation about how to accomplish it.
Given the edit to your question, there's a couple different ways to go about it. GrAnd has shown 2 of them. A third, and one I'm a fan of, is to use information specifically about how the command was called:
proc fun { File1 File2 {File3 ""}} { ;# file3 has a default
if {[llength [info level 0]] == 3} { ;# we were called with 2 arguments
;# (proc name is included in [info level 0])
# do what you need to do if called as [fun 1 2]
} else { ;# called with 3 arguments
# do what you need to do if called as [fun 1 2 3]
}
}

Here is an example to hack puts, using a namespace to hide puts and :: to access built-in:
namespace eval newNameSpace {
proc puts {arg} {
set tx "ADDED:: $arg"
::puts $tx
}
puts 102
}

Another way, you can do this:
proc example {
-file1:required
-file1:required
{-file3 ""}
} {
if {$file3 ne ""} {
#Do something ...
}
}
when you call the proc
example -fiel1 info -file2 info2

Related

Is there a workaround to pass variable number of arguments without using args

Here is the code:
>cat /tmp/test_args.tcl
proc t1 {args} {
return $args
}
proc t2 {args} {
puts "t2:[llength $args]"
return
set len [llength $args]
if {$len == 1} {
proc_len_1 [lindex $args 0]
} elseif {$len == 2} {
proc_len_2 [lindex $args 0] [lindex $args 1]
} else {
proc_len_x $args
}
}
set tup3 [t1 1 2 3 4 5 6]
puts "before calling t2:[llength $tup3]"
t2 $tup3
t2 100
t2 100 200
Here is the output:
>tclsh /tmp/test_args.tcl
before calling t2:6
t2:1
t2:1
t2:2
I am using TCL 8.6.
You can see that before calling t2, $tup3 is a list, but proc t2 receives $tup3 as one single value, so instead of a list of values, proc t2 receives a list of list of values.
But the intention of proc t2, as see in the code after "return", is to deal with various number of arguments and based on the number of arguments it does different things. Now, calling t2 with a list variable and with a literal are treated same. This is the problem.
The only solution I can think of is, change
t2 $tup3
to
t2 {*}$tup3
But I have a restriction: $tup3 needs to stay same when it is passed to different proc. E.g. I can have such proc which also expects $tup3:
proc t3 {arg1} {
}
t3 $tup3
So ideally if somehow I can make it that "args" does not wrap values into a list, then my problem is solved. Well, I know this is how TCL works.
Maybe I already answered my own question, or I do not know what the I am looking for. If you see indeed there is a solution, please let me know.
Thanks.
If you want to pass a list around, simply accept it as an argument:
proc a { mylist } {
b $mylist
}
proc b { mylist } {
foreach {k} $mylist {
puts $k
}
}
set tup3 [t1 1 2 3 4 5 6]
a $tup3
Edit:
For a variable number of arguments, using command line processing is easiest.
proc a { args } {
array set arguments $args
if { [info exists arguments(-tup3)] } {
puts "tup3: $arguments(-tup3)"
}
if { [info exists arguments(-tup1)] } {
puts "tup1: $arguments(-tup1)"
}
parray arguments
}
set tup3 [list 1 2 3 4 5 6]
a -tup1 test1 -tup3 $tup3 -tup2 test2
Tcl, by design, makes it very difficult for a procedure (or C-defined command) to examine the syntax of how it was called. It's totally deliberate that it is that way, as it makes it massively easier to compose commands arbitrarily. Commands that need to care especially about the syntax of how they're called are recommended to perform an extra step to process their argument, with appropriate calls to do things in the environment of the caller (trivial in C, slightly trickier in Tcl procedures because of the extra stack frame).
proc example inputString {
# Parse the string and work out what we want to do
if {[regexp {^\$(\w+)$} $inputString -> varName]} {
upvar 1 $varName value
} else {
set value $inputString
}
# Do something with the result
puts "my input string was '$inputString'"
puts "my value is '$value'"
catch {
puts "its length is [llength $value]"
}
}
example {foo bar boo}
set x 123
example {$x}
This prints:
my input string was 'foo bar boo'
my value is 'foo bar boo'
its length is 3
my input string was '$x'
my value is '123'
its length is 1
You can get the calling syntax inside your procedure, but this is highly unrecommended except for debugging as it tends to produce information that is usually annoying to process. Here's how you get it:
proc example inputString {
puts "I was called as: [dict get [info frame -1] cmd]"
}
# To show why this can be awkward, be aware that you get to see *all* the details...
example {foo bar boo}
example "quick brown fox"
example [expr {1 + sqrt(rand())}]
set x 123
example $x
Which prints:
I was called as: example {foo bar boo}
I was called as: example "quick brown fox"
I was called as: example [expr {1 + sqrt(rand())}]
I was called as: example $x
The first approach above, passing in a literal that you parse yourself (with appropriate help from Tcl as required) is considered to be good Tcl style. Embedding a language inside Tcl (which can be Tcl itself, or some other language; people have shown this working with embedded C and Fortran, and there's no reason to expect any other language to be a big problem, though getting useful evaluation semantics can sometimes be… tricky) is absolutely fine.

How to find a procedure by using the code inside the proc?

Is it possible to find the procedure name by using the content of that procedure?
For example,
proc test {args} {
set varA "exam"
puts "test program"
}
Using the statement set varA, is it possible to find its procedure name test?
Because, I need to find a procedure for which i know the output [it's printing something, i need to find the procedure using that].
I tried many ways like info frame, command. But, nothing helps.
Is it possible to find the procedure name by using the content of that procedure?
Yes. You use info level 0 to get the argument words to the current procedure (or info level -1 to get its caller's argument words). The first word is the command name, as resolved in the caller's context. That might be enough, but if not, you can use namespace which inside an uplevel 1 to get the fully-qualified name.
proc foo {args} {
set name [lindex [info level 0] 0]
set FQname [uplevel 1 [list namespace which $name]]
# ...
}
Note that this does not give you the main name in all circumstances. If you're using aliases or imported commands, the name you'll get will vary. Mostly that doesn't matter too much.
With info proc, we can get the content of a procedure which may helps you in what you expect.
The following procedure will search for the given word in all the namespaces. You can change it to search in particular namespace as well. Also, the search word can also be case insensitive if altered in terms of regexp with -nocase. It will return the list of procedure names which contains the search word.
proc getProcNameByContent {searchWord} {
set resultProcList {}
set nslist [namespace children ::]; # Getting all Namespaces list
lappend nslist ::; # Adding 'global scope namespace as well
foreach ns $nslist {
if {$ns eq "::"} {
set currentScopeProcs [info proc $ns*]
} else {
set currentScopeProcs [info proc ${ns}::*]
}
foreach myProc $currentScopeProcs {
if {[regexp $searchWord [info body $myProc]]} {
puts "found in $myProc"
lappend resultProcList $myProc
}
}
}
return $resultProcList
}
Example
% proc x {} {
puts hai
}
% proc y {} {
puts hello
}
% proc z {} {
puts world
}
% namespace eval dinesh {
proc test {} {
puts "world is amazing"
}
}
%
% getProcNameByContent world
found in ::dinesh::test
found in ::z
::dinesh::test ::z
%

TCL procedure call with optional parameters

There is a TCL script which has multiple procedure definitions with similar name func in different namespaces. Procedures look like this:
proc func {a} {
puts $a
}
All that kind of procedures have only one argument a . All that kind of procedures are called from one single line in whole script:
func $a
I need to create another procedure definition also with similar name func in other namespace. But that procedure will have two parameters. That procedure is also need to be called from the same line that other procedures with same name. Procedure looks like this:
proc func {a b} {
puts $a
puts $b
}
I need now to modify the line that calls all that procedures func $a so, that it can call all procedures with one parameter and new procedure which has two parameters. But procedures definitions with one parameter must not be changed. What line that calls all these procedures func $a should look like?
If you want an optional parameter, and you know what the optional value should be if not supplied, you do this:
proc func {a {b "the default"}} {
puts "a is $a"
puts "b is $b"
}
If you need to compute the default value at runtime, the simplest technique is a magic sentinel value that is very unlikely to occur in real input. Such as two ASCII NUL characters (== Unicode U+000000):
proc func {a {b "\u0000\u0000"}} {
if {$b eq "\u0000\u0000"} {
set b "default:$a"
}
puts "a is $a"
puts "b is $b"
}
Otherwise, you can use the magic args value to get the complete list of arguments and do all the work “by hand”:
proc func {a args} {
if {[llength $args] == 0} {
set b "the default..."
} elseif {[llength $args] == 1} {
set b [lindex $args 0]
} else {
error "bad number of arguments!"
}
puts "a is $a"
puts "b is $b"
}
If you're doing that, the info level introspector can help, but things can get complicated…
To call one of two implementations of a command depending on the number of arguments is rather unusual in Tcl code. You can do it providing neither implementation of the command is in the global namespace and you are not wanting the switching behaviour when calling from the namespaces containing the implementations in question.
What you do is you create a procedure in the global namespace (which every other namespace will look for commands in if not present locally) which then chains explicitly to the desired implementation. The main thing you need to enable this is some way of working out which implementation you want in a particular case (such as looking at the length of the argument list).
For Tcl 8.6, you can use tailcall for the chaining for maximum efficiency:
proc ::func args {
if {[llength $args] == 1} {
tailcall ::impl1::func {*}$args
} else {
tailcall ::impl2::func {*}$args
}
}
In Tcl 8.5 you'd write this instead (which is an optimised case in the interpreter):
proc ::func args {
if {[llength $args] == 1} {
return [uplevel 1 [list ::impl1::func {*}$args]]
} else {
return [uplevel 1 [list ::impl2::func {*}$args]]
}
}
In older Tcl versions, you'd use something like this (which is slower):
proc ::func args {
if {[llength $args] == 1} {
return [uplevel 1 ::impl1::func $args]
} else {
return [uplevel 1 ::impl2::func $args]
}
}
None of this is perfect at handling getting the right sort of error messages when you call with entirely the wrong number of arguments, especially if neither implementation formally has optional arguments. Determining that automatically is probably wholly impractical! You end up having to write extra boilerplate code (which is pretty obvious and works in all versions of Tcl in a straight-forward way):
proc ::func args {
if {[llength $args] == 1} {
tailcall ::impl1::func {*}$args
} elseif {[llength $args] == 2} {
tailcall ::impl2::func {*}$args
} else {
# Using the -errorcode is optional really
return -code error -errorcode {TCL WRONGARGS} \
"wrong # args: should be \"func a ?b?\""
}
}
I found the solution from that answer: https://stackoverflow.com/a/22933188/1601703 . We can get the number of argument that procedure accepts and make coresponding if statments that will use corresponding procedure call:
set num [llength [info args func]]
if {$num == 1} {
func $a
} elseif {$num == 2} {
func $a $b
}

Usage of namespace/uplevel/global in TCL

I have a script like this :
proc subProc1 { } {
puts $var1
}
proc subProc2 { } {
puts $var2
}
proc mainProc { args } {
# Define many variables
subProc1
subProc2
#etc.
}
I would like subProc1 and subProc2 to have variables defined in mainProc. I can pass them as arguments, but it is a lot of argument, I'd like to avoid that.
I tried to use the upvar command, by adding this line to me subProcs :
subProc1 { } {
upvar $var1 var1 $var2 var2 ;#etc
puts $var1
# etc.
}
But I have the "no such variable" error message, and it not nice to have a huge line like this
I just read about namespace but I don't really understand how to use this (plus I am not sure to understand the concept, so is it really adapted to my use case ?)
upvar is the right tool for that. The other commands can be emulated with upvar.
But you do a mistake how you call upvar. You have to use the variable name, not it's value (which will throw an "no such variable" error).
upvar var1 var1 var2 var2 ;#...
I'd think about using some different way to store the data, maybe a dictionary or an array?
This would make it easier to pass the variables.

Writing procedures in TCL

I am very new for TCL. Just I want to know that how to write TCL procedures without argument and how to call and how to execute it.
To write a procedure that doesn't take any arguments, do this:
proc someName {} {
# The {} above means a list of zero formal arguments
puts "Hello from inside someName"
}
To call that procedure, just write its name:
someName
If it was returning a value:
proc example2 {} {
return "some arbitrary value"
}
Then you'd do something with that returned value by enclosing the call in square brackets and using that where you want the value used:
set someVariable [example2]
To execute it... depends what you mean. I assume you mean doing so from outside a Tcl program. That's done by making the whole script (e.g., theScript.tcl) define the procedure and do the call, like this:
proc example3 {} {
return "The quick brown fox"
}
puts [example3]
That would then be run something like this:
tclsh8.5 theScript.tcl
You can define a procedure like this:
proc hello_world_proc {} {
puts "Hello world"
}
And you can execute it by simply writing:
hello_world_proc
If you want to use a return value of the procedure, you can do:
# Procedure declaration
proc hello_world_proc2 {} {
return "Hello world"
}
# Procedure call
puts [hello_world_proc2]
proc myProc {} {
# do something
}
# call proc
myProc
Te official Tcl website has some documentation on functions (procedures) that could help you at https://www.tcl.tk/man/tcl/TclCmd/proc.htm.
Procedure with no argument
If you don't need any argument here is how to write the procedure you want:
proc funcNameNoArgs {} {
puts "Hello from funcNameNoArgs"
}
And you can call it as follows:
funcNameNoArgs
Procedure with arguments
Now let's say you need arguments in the future. Here is the way to write that precedure in TCL:
proc funcNameWithArgs {arg1 arg2 arg3} {
puts "Hello from funcNameWithArgs "
}
You can call that function by doing:
funcName arg1 arg2 arg3
Here is a piece of code for you to try!
Remember to define functions before you call them, or you will get an error.
Try to copy paste this code in your interpreter to get started and play with it:
proc funcNameNoArgs {} {
puts "Hello from a function with no arguments"
}
funcNameNoArgs
proc funcNameWithArgs {arg1 arg2 arg3} {
puts "Hello from a function with 3 arguments"
puts $arg1
puts $arg2
puts $arg3
}
funcNameWithArgs "Argument 1" "Argument 2" "Argument 3"
Syntax of procedure
proc <Name Of procedure> {No of arguments, if u want don't need simply left empty} {
<Body>
}
Let See the Example:
Without Arguments:
proc Hello_eg { } { puts "Hello I M In procedure" }
How to run:
step 1: write tclsh on prompt
step 2: write the procedure as per above mention
step 3: write just the procedure name (i.e Hello_eg) to run the procedure
2.With Arguments:
proc Hello_Arg { first second }
{
puts "The first argument is: $first"
puts "The Second argument is: $second"
}
How to run this:
step 1: write tclsh on prompt
step 2: write the procedure as per above mention
step 3: write just the procedure name with arguments (i.e Hello_Arg Ramakant Singla) to run the procedure
It's pretty simple.
Defining :
proc myproc {} {
}
calling :
myproc
Since you are New, I advise you to go through tutorial point. They have simple and consolidated content.
Procedure is a set of statements which is being preapeated in a program.
Syntax
proc <Name> {INPUTS} {
BODY
}
Eg:
proc add {m n} {
set s 0
set s [expr $m + $n]
return $s
}
#Main Program Starts Here
set x 2
set y 3
set Result [add $x $y]
puts "$Result"
In the above example....in procedure we have provide a name (add) to the set of statements which can be call in the main program.
Any amount of arguments
What maybe would come in handy is using args.
By using args you can pass any amount of arguments to your procedure.
proc withAnyNumberOfArguments {args} {
if {$args eq ""} {
puts "got no arguments"
}
foreach arg $args {
puts "got $arg"
}
}
Optional Arguments
Another tip: Enclosing arguments with { } makes them optional arguments.
proc atLeastOneArgument {a1 {args}} {
puts -nonewline "got a1=$a1"
foreach arg $args {
puts -nonewline " and $arg"
}
puts "."
}
Default Values
If you want to have default values you can specify them as follows:
proc putsTime { {secondsSinceBeginOfEpoch "now"} } {
if {$secondsSinceBeginOfEpoch eq "now"} {
set secondsSinceBeginOfEpoch [clock seconds]
}
return [clock format $secondsSinceBeginOfEpoch]
}
Some Example Calls
1 % withAnyNumberOfArguments
got no arguments
2 % withAnyNumberOfArguments one
got one
3 % withAnyNumberOfArguments ready steady go!
got ready
got steady
got go!
4 % atLeastOneArgument "this is one argument" ;# because its in double quotes
got a1=this is one argument.
5 % atLeastOneArgument 3 2 1 go!
got a1=3 and 2 and 1 and go!.
6 % puts [formatTime]
Fri Dec 18 16:39:43 CET 2015
7 % puts [formatTime 0]
Thu Jan 01 01:00:00 CET 1970
In addition to the answers above, I would recommend using tcltutor.exe (available from http://tcltutor.software.informer.com/3.0b/) to learn TCL.
It'll have a chapter on Subroutines that'll help you define a TCL proc without and with arguments.
Regards
Sharad
To create a TCL procedure without any parameter you should use the proc keyword followed by the procedure name then the scope of your procedure.
proc hello_world {} {
// Use puts to print your output in the terminal.
// If your procedure return data use return keyword.
}
You can use the created procedure by simply calling its name:
hello_world
This solution is based on previous questions about writing procs. I personally feel this is one of the better ways to write a procedure in tcl.
Code
proc sampleProc args {
# Defaults
array set options {-device router0 -ip "10.16.1.62"}
# Read args
array set options $args
# Assign
set device $options(-device)
set ip $options(-ip)
# Usage
puts "Device under use is $device and IP is $ip"
# Return
return "${sd} :: $ip"
}
Execution
tclsh> source sampleProc.tcl
Device under use is router0 and IP is 10.16.1.62
router0 :: 10.16.1.62