Version 1 of Constraining variables

Updated 2002-11-15 16:03:07

set docu(constrain) {Richard Suchenwirth - Here's an application of write traces to constrain a variable's value to either a range (numeric or string prefix, joined with two dots) or a list of permitted values, that work on scalars, arrays or array elements. Examples:

   constrain x 0..1023
   constrain y {yes no maybe}
   constrain z(mode) A..Z

If a constraint is violated, an error is raised with a descriptive message:

 can't set "y": value 'yesSir' outside constraint 'yes no maybe'

As usual with traces, constraints are deleted if the variable is unset. If you still want to set an out-of-constraint value, just catch the assignment:

 catch {set y "definitely"}

This is because write traces fire after the assignment, so the "bad" value is there anyway. Numeric constraints don't distinguish between int and double values; string prefixes are just that, so "foo" is inside the range "a..z" even if it's not a single character.

You can introspect the constraints you have set by calling

 constrain name

which returns the active constraint in the same format, or an empty string if the variable is not constrained. Also, you can remove a constraint by overriding it with an empty string:

 constrain name ""

}

 proc constrain {var {cond -}} {
    if {$cond=="-"} {
        set trace [lindex [lindex [uplevel 1 trace vinfo $var] end] end]
        return [join [lrange $trace 1 end] ..]
    }
    foreach i [uplevel 1 trace vinfo $var] {
        if [regexp constrain $i] {uplevel 1 trace vdelete $var $i} 
    }
    if {$cond==""} return
        if [regexp {(.+)[.][.](.+)} $cond -> from to] {
                set trace [list constrainRange $from $to]
        }  else {
            set trace [list constrainList $cond]
        }
        uplevel 1 trace var $var w [list $trace]
 }
 proc constrainRange {from to _var el op} {
        set name $_var[expr {$el!=""? "($el)": ""}]
        upvar 1 $name var
        if {$var<$from || $var>$to} {
                return -code error "value $var outside constraint $from..$to"
        }
 }
 proc constrainList {list _var el op} {
        set name $_var[expr {$el!=""? "($el)": ""}]
        upvar 1 $name var
        if {[lsearch $list $var]<0} {
                return -code error "value '$var' outside constraint '$list'"
        }
 }

See also constants for traces used to prevent a variable value from being changed, and type checking.


Category Concept | Arts and crafts of Tcl-Tk programming