Updated 2016-07-17 10:13:21 by dkf

Larry Smith - This page supersedes getparams. It is the latest and most elegant version of my named parameter parsing routine. It takes an option -using <overrides> parameter, and a list of vars and default values. First it initializes the variables to their given defaults in the calling scope, then it parses the overrides argument and sets those "-var val" pairs in the calling scope. In essence, it declares all your local variables and then allows them to be set by command-line switches. But it will not permit vars mentioned in the overrides being set unless they appear in the vars list. This prevents the caller from setting local or meaningless variables.
# init.tcl
# Takes a list of variable-value pairs and creates and
# initializes the former with the latter in the calling
# context.

proc init {args} {
  upvar args arglist
  set assign {
    if {[string index $val 0] eq "&"} {
      set val [string range $val 1 end]
      catch {uplevel unset $var}
      uplevel upvar $val $var
    } else {uplevel 1 set $var \{$val\}}
  }
  set args [regsub -all {\#[[:print:]]*} $args "\r"]
  if {[llength $args]==0} return
  if {[llength $args]==1} {eval set args $args}
  # first pass - args on command line
  set vars {}
  foreach {var val} $args {
    eval $assign
    lappend vars $var
  }
  set i 0
  foreach var $vars {
    set i [lsearch $arglist -$var]
    if {$i != -1} {
      set arglist [lreplace $arglist $i $i]
      if {$i <= [llength $arglist]} {
        set val [lindex $arglist $i]
        set arglist [lreplace $arglist $i $i]
      } else {
        set val 1
      }
    } else {incr i; continue}
    eval $assign
  }
  set arglist [string trim $arglist]
}

Examples edit

proc foo { args } {
   init a 1 b 2 c 3
   puts "$a, $b, $c"
}

foo -a 3

prints "3, 2, 3"
foo -b 1 -c 4

prints "1, 1, 4"
foo -b 1 -d 5

prints "1, 1, 3" and the attempt to set a var "d" is ignored (it did not appear in the init list).

You can also brace arguments:
foo -a 5 -b 2

can also be expressed as:
foo {
   -a 5   # another new feature is embedded comments!
   -b 2   # You can now document the parameters you pass.
}

The new feature - adding references to Tcl!
proc changevar {args} {
  init var ""
  set var 3
}

set foo 1
changevar -var &foo
puts "foo is now $foo"

This sets the global "foo" to 3.

init is now also much more conservative in how it handles args. In particular, it now only sets the variables mentioned in the command line - a call of "init a 1 b 2" not only sets just a and b, it also only removes -a x and -b x in the args list as well. This leaves the list intact for multiple calls to init depending on when and where in your code you decide you need another arg. A "parse-at-will" sort of deal.
proc demo {args} {
  init a 1 b 2

  # la-de-da
  # oops - we need c as well!
  init c 3
  puts "now we have $c"
}

demo -a 56 -b 27 -c 92

This is very helpful for evaluating code - the code that follows the "oops" line above is often code from some other source (even passed as an argument itself) with its own requirement for parameters. Now you can use this code and get parameters for it the same way as you would for a proc. This is extremely handy for dealing with code coming out of databases or the like.