TclOO Variable Scope with Inheritance/superclass

 ̄綄美尐妖づ 提交于 2019-12-07 08:46:27

The variable is physically located (if you can say that for code) in a private namespace for the object instance. By doing variable in the declaration, you are directing the method bindings to merely make it available without further commands.

But yes, subclasses have to use variable as well to see it by default, or to use one of the standard Tcl variable scope management commands, e.g., upvar or namespace upvar, or even the private variable method.

oo::class create smartToaster {
    superclass toaster
    method toast {nSlices} {
        my variable nCrumbs
        if {$nCrumbs > 40} {
            my clean
        }
        next $nSlices
    }
}
oo::class create smartToaster {
    superclass toaster
    method toast {nSlices} {
        namespace upvar [namespace current] nCrumbs nc
        if {$nc > 40} {
            my clean
        }
        next $nSlices
    }
}

It didn't used to be that way, but it was found to be just too confusing otherwise; the variable declarations for a class only affect the methods of that class, not its subclasses.


[EDIT]: It is possible to make a parent's variables also visible in the child via appropriate metaclass magic:

oo::class create Class {
    superclass oo::class
    constructor args {
        next {*}$args
        set cs [info class superclasses [self]]
        while {[llength $cs]} {
            set cs [concat [lassign $cs c] [info class superclasses $c]]
            oo::define [self] variable -append {*}[info class variables $c]
        }
    }
}

Demonstrating this:

% Class create foo {
    variable x
}
::foo
% Class create bar {
    superclass foo
    variable y
}
::bar
% Class create boo {
    superclass bar
    variable z
}
::boo
% info class variables boo
z y x

While I don't in general recommend this as it makes a subclass much more fragile when a superclass evolves, and it doesn't track any changes to the superclass, it's easy to set up with a little scripting. You just delegate the smartness to your own metaclass and use that to construct all your operational classes (which are purely conventional TclOO classes from there on).

Donal, thanks for answering.

So I am assuming there is no mechanism available by default to make all the superclass variables available.

My current solution is to collect all variable names and then declare them with one single call. Yet, I have to repeat this in every method. I would want to put the declareSuperclassVars outside the methods. Is this possible somehow? Perhaps with another approach?

Thanks

oo::class create toaster {
    variable nCrumbs;                    #declaration
    variable toTest;                     #another one nowhere defined

    constructor {} {
        set nCrumbs 0;                   #definition
    }

    method toast {nSlices} {
        if {$nCrumbs > 50} {
            error "== FIRE! FIRE! =="
        }
        set nCrumbs [expr $nCrumbs+4*$nSlices]
    }

    method clean {} {
        set nCrumbs 0
    }

    method declareSuperclassVars {} {
        my variable lSuperclassVars
        set lSuperclassVars [info vars]; #fill variable list
        uplevel 1 {
            my variable lSuperclassVars
            eval "my variable $lSuperclassVars"
        }
    }
}

oo::class create smartToaster {
    superclass toaster;                  #inherit

    #declareSuperclassVars;              #<======= would like to do it here

    method toast {nSlices} {
        my declareSuperclassVars;        #declare all at once

        if {$nCrumbs > 40} {
            my clean
        }
        next $nSlices; #call superclass method
    }
}

set clsToaster [smartToaster new]
$clsToaster toast 2
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!