ooptcl

How to write -get script for oo::configurable if property itself has name value


I have next code:

    oo::configurable create Temp {
    superclass SPICEElement
    property name -set {
        set name [string tolower $value]
    }
    property value -get {
        return [$value configure -value]
    }
    variable name value
    constructor {value args} {
        # Creates object of class `Temp`.
        #  value - value of the temperature
        #  args - optional parameter qualificator -eq
        # This class represent .temp statement with temperature value.
        set arguments [argparse {
            {-eq -boolean}
        }]
        my configure -name temp
        if {$eq} {
            my AddParam temp $value -eq
        } else {
            my AddParam temp $value
        }
    }
    method AddParam {paramName value args} {
        # Adds temperature parameter.
        #  paramName - name of temperature parameter
        #  value - value of temperature
        #  args - optional parameter qualificator -eq
        set arguments [argparse {
            {-eq -boolean} 
        }]
        if {$eq} {
            set value [::SpiceGenTcl::ParameterPositionalEquation new $paramName $value]
        } else {
            set value [::SpiceGenTcl::ParameterPositional new $paramName $value]
        }
        return
    }
    method genSPICEString {} {
        # Creates .temp statement string for SPICE netlist.
        # Returns: '.temp $Value'
        return ".temp [[my configure -value] genSPICEString]"
    }
}
oo::define Temp {
    method <WriteProp-value> val {
        lassign $val value eq
        if {$eq=="-eq"} {
            my AddParam temp $val -eq
        } elseif {$eq==""} {
            my AddParam temp $val
        } else {
            return -code error "Wrong value '$eq' of qualifier"
        }
    }    
}

Objects ::SpiceGenTcl::ParameterPositionalEquation and ::SpiceGenTcl::ParameterPositional have the property with the same name value. So,when I try to run next test:

test testTempClass-1 {test creation of Temp class instance and genSPICEString interface} -setup {
    set temp [Temp new 10]
} -body {
    set result [$temp genSPICEString]
} -result ".temp 10" -cleanup {
    unset temp result
}

I got the next error message:

    ---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: can't read "value": no such variable
    while executing
"$value configure -value"
    (class "::SpiceGenTcl::Temp" method "<ReadProp-value>" line 2)
    invoked from within
"my configure -value"
    (class "::SpiceGenTcl::Temp" method "genSPICEString" line 4)
    invoked from within
"$temp genSPICEString"
    ("uplevel" body line 2)

As you can see, I redefine the method <WriteProp-value> to allow use val name as variable name inside the setter. It works, but doesn't work for custom -get method, and I don't understand why. It should call object stored in value property with configure method of that object, and return the string stored in the value property of called object. I assume I should redefine the <ReadProp-value> instead of defining it in property, but it also failed with the same error.

Thank you in advance.


Solution

  • You need to have set the variable in the object in order to read it in the implementation of the property reader. So far, so simple. The issue is that the constructor isn't writing to the instance variable. The reason why is annoying.

    It is because the Tcl procedure compiler (methods reuse it, constructors are special methods) has resolved value to a formal argument and so doesn't even look at the instructions about that variable set up in the variable declaration. You need to set the variable by some other mechanism.

    Possible Fixes

    Any of these would go in the constructor.

    set [my varname value] $value
    

    Relies on the fact that the default varname method ignores local variables entirely by design. (It's intended for producing global handles for vwait and Tk widgets, but it works here just as well.)

    my configure -value $value
    

    If the property is writable, you can just write it. Local variables won't confuse it. I think this is more elegant, but it does constrain the property to be standard-model-writable.

    Or you can just alter the name of the argument to the constructor or the instance variable.