| [ Team LiB ] |
|
Tracing Variables and CommandsThe trace command registers a command to be called whenever a variable is accessed, modified, or unset. Tcl 8.4 introduced an updated trace command which includes support for command tracing. The original (and still supported) form of the command applies only to variable traces: trace variable name ops command trace vdelete name ops command trace vinfo name The name is a Tcl variable name, which can be a simple variable, an array, or an array element. If a whole array is traced, the trace is invoked when any element is used according to ops. The ops argument is one or more of the letters r, for read traces, w, for write traces, u, for unset traces, and a for array traces. The command is executed when one of these events occurs. It is invoked as:
command name1 name2 op
The name1 argument is the variable or array name. The name2 argument is the name of the array index, or null if the trace is on a simple variable. If there is an unset trace on an entire array and the array is unset, name2 is also null. The value of the variable is not passed to the procedure. The traced variable is one level up the Tcl call stack. The upvar, uplevel, or global commands need to be used to make the variable visible in the scope of command. These commands are described in more detail in Chapter 7. A read trace is invoked before the value of the variable is returned, so if it changes the variable itself, the new value is returned. A write trace is called after the variable is modified. The unset trace is called after the variable is unset. The array trace, which was added in Tcl 8.4, is called before the array command (e.g., array names) is used on the variable. A variable trace is automatically deleted when the variable is unset. Command TracingThe new form of trace supports both variable and command tracing: trace add type name ops command trace remove type name ops command trace info type name The type is one of command, execution or variable. For command, ops is a list and may contain rename, to trace the renaming of a Tcl command, or delete, to trace the deletion of a command. Command tracing cannot be used to prevent the actual deletion of a command, it just receives the notification. No command traces are triggered when an interpreter is deleted. The command is invoked as:
command oldName newName op
For execution, the ops may be any of enter, leave, enterstep, and leavestep. enter invokes command immediately before the command name is executed, and leave will invoke command immediately following each execution. enterstep and leavestep are similar but they operate on the Tcl procedure name, invoking command for each Tcl command inside the procedure. In order to do this, they prevent the bytecode compilation of that procedure. This allows you to create a simple debugger in pure Tcl. The enter and enterstep operations invoke command as:
command command-string op
The leave and leavestep operations invoke command as:
command command-string code result op
The command-string is the current command being executed, code is the result code of the execution and result is the result string. Example 6-16 on page 84 illustrates the different result codes. For variable tracing, the ops may be one or more of read, write, unset, or array. This is an alternate way to set up the variable traces described earlier. Read-Only VariablesExample 13-8 uses traces to implement a read-only variable. A variable is modified before the trace procedure is called, so the ReadOnly variable is needed to preserve the original value. When a variable is unset, the traces are automatically removed, so the unset trace action reestablishes the trace explicitly. Note that the upvar alias (e.g., var) cannot be used to set up the trace. Instead, uplevel is used to create the trace in the original context of the variable. In general, essentially all traces are on global or namespace variables. Example 13-8 Tracing variables
proc ReadOnlyVar {varName} {
upvar 1 $varName var
global ReadOnly
set ReadOnly($varName) $var
uplevel 1 [list trace variable $varName wu ReadOnlyTrace]
}
proc ReadOnlyTrace { varName index op } {
global ReadOnly
upvar 1 $varName var
switch $op {
w {
set var $ReadOnly($varName)
}
u {
set var $ReadOnly($varName)
# Re-establish the trace using the true name
uplevel 1 [list ReadOnlyVar $varName]
}
}
}
This example merely overrides the new value with the saved value. Another alternative is to raise an error with the error command. This will cause the command that modified the variable to return the error. Another common use of trace is to update a user interface widget in response to a variable change. Several of the Tk widgets have this feature built into them. If more than one trace is set on a variable, then they are invoked in reverse order; the most recent trace is executed first. If there is a trace on an array and on an array element, then the trace on the array is invoked first. Creating an Array with TracesExample 13-9 uses an array trace to dynamically create array elements: Example 13-9 Creating array elements with array traces
# make sure variable is an array
set dynamic() {}
trace variable dynamic r FixupDynamic
proc FixupDynamic {name index op} {
upvar 1 $name dynArray
if {![info exists dynArray($index)]} {
set dynArray($index) 0
}
}
Information about traces on a variable is returned with the vinfo option:
trace vinfo dynamic
=> {r FixupDynamic}
A trace is deleted with the vdelete option, which has the same form as the variable option. The trace in the previous example can be removed with the following command: trace vdelete dynamic r FixupDynamic |
| [ Team LiB ] |
|