Most people have parsed command lines in C with long switch statements that list each option, and what variable to set if that option is selected. The same technique can be used in Tcl, but requires two changes whenever a variable is added, or a new setting is needed: One to the code that uses the variable, and one to the ever-growing switch statement for parsing the command line.
Alternatively, the way that variable substitution is performed in Tcl allows
a simple construct for setting variables.
I use a convention that command line arguments are of the form:
-SindexName Value
. This code could be used to set state variables:
for {set i 0} {$i < $argc} {incr i} {
set arg [lindex $argv $i]
if {[string first "-S"] != 0} {complain "Bad Argument: $arg"}
set indexName [string range $arg 2 end]
incr i;
set tutorGlobals($indexName) [lindex $argv $i]
}
This construct is appealing in that nothing needs to be changed to allow a new state variable to be set from the command line. However, I don't use this construct. This construct makes life just a little bit too easy, since state variables can be created without creating a default state for the variable. The construct I use resembles this:
;;#######################################################################
;;# proc CheckCmdLineArgs {}
;;# Evaluate the command line arguments, in particular, allows an global
;;# variable to be set from the command line.
;;#
proc CheckCmdLineArgs {} {
global tutorGlobalList; eval "global $tutorGlobalList";
;# iterate through the args,
foreach { name val } $argv {
if { [string first "-S" $name] == 0 } {
set name [string trimleft "-S" $name]
if { [info exists tutorGlobals($name)] } {
set tutorGlobals($name) $val
} else {
puts "Bad argument: $name $val"
}
}
}
}
Several of the techniques I use to simplify code maintenance are shown
in this proc. The first obvious item is the use of the ";;#" as a
comment marker. My convention is to precede normal comments with ";#",
and proc headers with ";;#". This lets me create overview documentation
with a simple: grep ";;#" module.tcl
. Being able to generate
overview documentation quickly and easily is a great boon to the code
maintainer. (In this case me.)
Next is the construct for setting the globals. The global
tutorGlobalList
provides a single location for adding new globals
as they become necessary. Updating that single string
makes the new variables available to all TclTutor procs. The prefix "tutor"
provides name-space control.
The command line parsing is done in the loop that checks for pairs of arguments in the form "-SvarName value", tests to be certain that the variable has been defined, and assigns the value to that variable.
When a default value is defined for a state variable, it automatically becomes possible to set that value from the command line. However, it's not possible to add a variable, test it from the command line, neglect to provide a default value and get surprised later when code starts crashing.