theLib 10.0
NHI1 -
theKernel -
theLink -
theConfig -
theSq3Lite -
theCompiler -
theBrain -
theGuard -
theLib
|
myoo is a TCL extension used for Object-Oriented-Programming (OOP).
There are two versions of the myoo library ...
tclmyoo only uses the tcl-standart-api-commands to implement myoo.
A CLASS is a cls
array in the CLASS-NS :
::
variable
in the CLASS-NS.proc
in the CLASS-NS.proc
called like CLASS-NAME with a INSTANCE-NS as first argument.proc
called like ~CLASS-NAME with a INSTANCE-NS as first argument.proc
with namespace export
attribute.proc
without INSTANCE-NS as first argument.proc
wit INSTANCE-NS as first argument.proc
.An INSTANCE is a my
array in the INSTANCE-NS :
::
my
array.namespace upvar INSTANCE-NS my otherVar
libmyoo always loads tclmyoo first and then replaces the performance-relevant procedures with C code.
Although tclmyoo and libmyoo each use only one page of code, it has been shown that the application performs better than the traditional tcloo.
It should also be noted that translating an application written with tclmyoo into C-only code with the Tcl-2-C-Compiler is possible at any time.
The central development goal for libmyoo was to implement Object-Oriented-Programming (OOP) in TCL with the minimal possible effort combined with the maximum possible execution speed.
The difference between tcloo and myoo :
The libmyoo is available as TCL loadable package :
# Load the the tcl extension with : package require tclmyoox > 1.0 # The package 'tclmyoox' has a private namespace called '::myooX' print myooX > myooX<namespace> | variables | * : info : vars | ----------- | -------------------------------------------------------------------------------------------------- | public-own | CreateI : DestroyI : SuperI | ----------- | -------------------------------------------------------------------------------------------------- | private-own | ClassIsN : ClassN : Create1N : Create2N | <> | Create3N : CtorN : DestroyN : MakeN | <> | NewN : RecompileN : ResetN : SuperN | <> | _ErrorCatch : _ErrorCatchCtorFailedN : _ErrorCatchDtorFailedN : _ErrorRaise | <> | _ErrorRaiseCtorInvalidArgumentN | ----------- | --------------------------------------------------------------------------------------------------
A class is a namespace with an array of name 'cls'
# Create the class of name ::MyClass : # REMEMBER: The 'ClassN' command return the CLASS-NS. ::myooX::ClassN ::MyClass > ::MyClass # The CLASS-REF is initial filled with OO specific items : print -list ::MyClass > LIST | ::MyClass : <namespace> | : variables : * : info : vars | -------------------- : --------------------------------- # A more complex example using multiple classes ::myooX::ClassN ::AnotherClass { # A CLASS-attribute is just a normal namespace variable variable clsVar "hallo" # ::MyClass is the super-class of ::AnotherClass SuperI ::MyClass # The constructor is called like the class proc AnotherClass { myNs someVal } { # Access the CLASS-variable variable clsVar # Make the INSTANCE-NS to a LOCAL-REF namespace upvar $myNs my my # intitialize the INSTANCE-ATTRIBUTE called 'val' set my(val) $someVal-$clsVar } # The destructor is called like the constructor but with '~' proc ~AnotherClass {} { # Free a ressource } } > ::AnotherClass # A class is a 'singelton' and is located always in the CLASS-NS : print -list ::AnotherClass > LIST | ::AnotherClass : <namespace> | : variables : * : info : vars | : public-own : AnotherClass : ~AnotherClass | -------------------- : ----------------------------------------------- # A class can be modified from outside ... proc ::MyClass::MyClass {otherArg} { # ... } > # but have to be recompiled afterwords. ::myooX::RecompileN ::MyClass > ::MyClass print ::MyClass > ::MyClass<namespace> | variables | * : info : vars | public-own | MyClass
An instance is a namespace with an array of name 'my'
# An instance always require a class ::myooX::ClassN ::MyClass { proc MyClass { myNs someVal } { namespace upvar $myNs my my set my(val) $someVal-123 } proc getVal {myNs} { namespace upvar $myNs my my set my(val) } } > ::MyClass # =========================================================================================== # create an instance # By default a 'instance' created with 'NewN' is always located in the namespace of the class : set myNs [::myooX::NewN ::MyClass abc] > ::MyClass::MyClass-1 # It is an error if the arguments to 'NewN' are incompatible with the arguments of the class-constructor : ::myooX::NewN ::MyClass abc def > error: in itpEvalDirect [CtorN] call CTOR failed for instance '::MyClass::MyClass-2'. | wrong # args: should be "::MyClass::MyClass myNs someVal" | while executing | "::MyClass::MyClass ::MyClass::MyClass-2 abc def" while executing "::myooX::NewN ::MyClass abc def" invoked from within "interp eval $itp $cmd" # The namespace of ::MyClass has now a new child because 'NewN' create a annonymous instance : print ::MyClass > ::MyClass<namespace> | variables | * : info : vars | children | ::MyClass::MyClass-1 | public-own | MyClass | private-own | getVal # It is also possible to create a NAMED-INSTANCE in the namespace of the CLASS or in the namespace specified : ::myooX::Create2N ::MyClass ::otto 998 > ::otto # Same procedure as ... print ::otto > ::otto<namespace> | variables | * : info : vars
Task : create and initialize an INSTANCE with SUPER-class
# first class # load tcl-only extension package require tclmyoox > 1.0 # define the class (super-class) ::myooX::ClassN ::classA { # CLASS-ATTRIBUTE variable counter 0 # ONLY export aProc (CTOR & DTOR are exported always) namespace export -clear aProc # CTOR proc classA {myNs name} { # access INSTANCE attribute namespace upvar $myNs my my # set INSTANCE-ATTRIBUTE set my(nameA) "(aVar:$name)" } # a method proc aProc {myNs} { # access INSTANCE attribute namespace upvar $myNs my my # access CLASS-ATTRIBUTE variable counter incr counter return "(aProc\[%$counter]:$my(nameA))" } } > ::classA #second class ::myooX::ClassN ::classB { # CLASS-ATTRIBUTE variable counter 0 # add super-class SuperI ::classA # constructor proc classB { myNs name } { # create LOCAL-REF namespace upvar $myNs my my # the pogrammer HAVE to call the CONSTRUCTOR of the SUPER-CLASS classA $myNs "(bCtor:$name)" # initialize INSTANCE-ATTRIBUTE set my(nameB) "(bVar:$name)" } # a method proc bProc {myNs} { # create LOCAL-REF namespace upvar $myNs my my # access CLASS-ATTRIBUTE variable counter # modify CLASS-ATTRIBUTE incr counter # always use the LOCAL-REF because it is already resolved return "(bProc\[%$counter]:[aProc $myNs]-|-$my(nameB))" } } > ::classB # create an annonymous INSTANCE and return INSTANCE-NS set myNs [::myooX::NewN ::classB veggi] > ::classB::classB-1 # resolve INSTANCE-NS into a LOCAL-REF namespace upvar $myNs my myR > ::classB::aProc $myNs ; # call aProc with INSTANCE-NS > (aProc[%1]:(aVar:(bCtor:veggi))) ::classB::bProc $myNs ; # call bProc with INSTANCE-NS > (bProc[%1]:(aProc[%2]:(aVar:(bCtor:veggi)))-|-(bVar:veggi)) ::classB::aProc $myR(__NS__) ; # call aProc with LOCAL-REF > (aProc[%3]:(aVar:(bCtor:veggi))) ::classB::bProc $myR(__NS__) ; # call bProc with LOCAL-REF > (bProc[%2]:(aProc[%4]:(aVar:(bCtor:veggi)))-|-(bVar:veggi)) # 1. show the REFERENCE and the NAMESPACE of the LOCAL-REF print -list myR $myR(__NS__) > LIST | myR : <instance> | : __CLASS__ : ::classB | : __NAME__ : classB-1 | : __NS__ : ::classB::classB-1 | : nameA : (aVar:(bCtor:veggi)) | : nameB : (bVar:veggi) | -------------------- : ----------------------------------- | ::classB::classB-1 : <namespace> | : variables : * : info : vars | -------------------- : --------------------------------- # 2. show the REFERENCE and the NAMESPACE of the INSTANCE-NS print -list $myNs > LIST | ::classB::classB-1 : <namespace> | : variables : * : info : vars | -------------------- : --------------------------------- # 3. show the REFERENCE and the NAMESPACE of the INSTANCE-class print -list $myR(__CLASS__) > LIST | ::classB : <namespace> | : variables : * : info : vars | : children : ::classB::classB-1 | : public-own : classB | : private-own : bProc | : private-imp : aProc : classA | -------------------- : --------------------------------------
Task : create an INSTANCE in the namespace of another INSTANCE
package require tclmyoox > 1.0 # first class ::myooX::ClassN ::classA { # ONLY export aProc (CTOR & DTOR are exported always) namespace export -clear aProc # CTOR proc classA {myNs name} { namespace upvar $myNs my my # set INSTANCE-ATTRIBUTE set my(nameA) $name-in-A } # DTOR proc ~classA {myNs} { # cleanup } proc aProc {myNs} { namespace upvar $myNs my my return "aProc-|-$my(nameA)" } } > ::classA #second class ::myooX::ClassN ::classB { # CTOR proc classB { myNs name } { namespace upvar $myNs my my # set INSTANCE-ATTRIBUTE set my(nameB) $name-in-B # create the EMBEDDED-INSTANCE with name 'embA' and class '::classA' into the NAMESPACE of instance 'my' # REMEMBER: if the CURRENT instance (my) is deleted the EMBEDDED instance (embA) is also deleted : CreateI $myNs ::classA embA ::classA $name-add-to-A } # DTOR proc ~classB { myNs } { namespace upvar $myNs my my # destroy the EMBEDDED instance # REMEMBER: 'embA' is the EMBEDDED-INSTANCE DestroyI $myNs $my(embA) } proc bProc { myNs } { namespace upvar $myNs my my return [::classA::aProc $my(embA)]-|-$my(nameB) } } > ::classB # ====================================================================================== # ANNONYMOUS instance # create annonymous INSTANCE # REMEMBER: the ANNONYMOUS-INSTANCE is created in the NS of the class with 'cls(__NAME__)-__INDEX__' as name set myNs [::myooX::NewN ::classB veggi] > error: in itpEvalDirect [CtorN] call CTOR failed for instance '::classB::classB-1'. | [CtorN] call CTOR failed for instance '::classB::classB-1::embA'. | | wrong # args: should be "::classA::classA myNs name" | | while executing | | "$cls(__CTOR__) $myNs {*}$argsL" | while executing | "CtorN $clsNs [MakeN $clsNs $Xname $Xns] $args" | (procedure "Create3N" line 2) | invoked from within | "Create3N $clsNs $embName $myNs {*}$args" # access a LOCAL-REF is faster than the INSTANCE-NS because it is already resolved. namespace upvar $myNs my myR > error: in itpEvalDirect can't read "myNs": no such variable while executing "namespace upvar $myNs my myR" invoked from within "interp eval $itp $cmd" # call "bProc" with INSTANCE-NS time {::classB::bProc $myNs} 1000 > error: in itpEvalDirect can't read "myNs": no such variable while executing "::classB::bProc $myNs" invoked from within "time {::classB::bProc $myNs} 1000" # 1. show the REFERENCE and the NAMESPACE of the INSTANCE # REMEMBER: the EMBEDDED-NAMESPACE 'embA' is now a CHILD-NAMESPACE of the INSTANCE print -list myR $myR(__NS__) > error: in itpEvalDirect can't read "myR(__NS__)": no such variable while executing "print -list myR $myR(__NS__)" invoked from within "interp eval $itp $cmd" # 2. show the REFERENCE and the NAMESPACE of the INSTANCE-class # REMEMBER: the CLASS-ATTRIBUTE __INDEX__ is incremented by one # REMEMBER: the INSTANCE-NAMESPACE 'classB-1' is now a CHILD-NAMESPACE of the class print -list $myR(__CLASS__) > error: in itpEvalDirect can't read "myR(__CLASS__)": no such variable while executing "print -list $myR(__CLASS__)" invoked from within "interp eval $itp $cmd" # 3. show the REFERENCE and the NAMESPACE of the embedded INSTANCE in 'classB' print -list $myR(embA) > error: in itpEvalDirect can't read "myR(embA)": no such variable while executing "print -list $myR(embA)" invoked from within "interp eval $itp $cmd" # To access the embedded INSTANCE a LOCAL-REF is also possible namespace upvar $myR(embA) my embAR > error: in itpEvalDirect can't read "myR(embA)": no such variable while executing "namespace upvar $myR(embA) my embAR" invoked from within "interp eval $itp $cmd" # same as above but use the LOCAL-REFERENCE print -list embAR $embAR(__NS__) > error: in itpEvalDirect can't read "embAR(__NS__)": no such variable while executing "print -list embAR $embAR(__NS__)" invoked from within "interp eval $itp $cmd" # delete the INSTANCE, call the __DTOR__ ::myooX::DestroyN $myNs > error: in itpEvalDirect can't read "myNs": no such variable while executing "::myooX::DestroyN $myNs" invoked from within "interp eval $itp $cmd" # the INSTANCE-ATTRIBUTE 'embA' and the EMBEDDED-NAMESPACE 'embA' are now deleted print myR > myR # ====================================================================================== # NAMED instance # every new INSTANCE is created by default in the NS of the class this also include the NAMED instance set myNs2 [::myooX::Create2N ::classB otto something] > error: in itpEvalDirect [CtorN] call CTOR failed for instance '::classB::otto'. | [CtorN] call CTOR failed for instance '::classB::otto::embA'. | | wrong # args: should be "::classA::classA myNs name" | | while executing | | "$cls(__CTOR__) $myNs {*}$argsL" | while executing | "CtorN $clsNs [MakeN $clsNs $Xname $Xns] $args" | (procedure "Create3N" line 2) | invoked from within | "Create3N $clsNs $embName $myNs {*}$args" namespace upvar $myNs2 my myR2 > error: in itpEvalDirect can't read "myNs2": no such variable while executing "namespace upvar $myNs2 my myR2" invoked from within "interp eval $itp $cmd" # LOCAL-REF of the INSTANCE and CLASS of the INSTANCE # REMEMBER: the CLASS-NS has now a new CHILD-NS with name "otto" # REMEMBER: the CLASS-ATTRIBUTE __INDEX__ is only incremented if a new ANNONYMOUS-INSTANCE is created print -list myR2 $myR2(__CLASS__) > error: in itpEvalDirect can't read "myR2(__CLASS__)": no such variable while executing "print -list myR2 $myR2(__CLASS__)" invoked from within "interp eval $itp $cmd" # ====================================================================================== # ABSOLUT NAMED instance # the new INSTANCE with NAMED-NS is created # REMEMBER: '::hermann' is the NAMESPACE ::myooX::Create2N ::classB ::hermann other > error: in itpEvalDirect [CtorN] call CTOR failed for instance '::hermann'. | [CtorN] call CTOR failed for instance '::hermann::embA'. | | wrong # args: should be "::classA::classA myNs name" | | while executing | | "$cls(__CTOR__) $myNs {*}$argsL" | while executing | "CtorN $clsNs [MakeN $clsNs $Xname $Xns] $args" | (procedure "Create3N" line 2) | invoked from within | "Create3N $clsNs $embName $myNs {*}$args" # the NAMED-NS has an array called 'my' namespace upvar ::hermann my myR3 > error: in itpEvalDirect namespace "::hermann" not found while executing "namespace upvar ::hermann my myR3" invoked from within "interp eval $itp $cmd" # both REFERENCES are equal but 'myR3' is already resolved print -list ::hermann::my myR3 > ::hermann::my , LIST | info : myR3 # the EMBEDDED-INSTANCE is now at '::hermann' print -list ::hermann ::hermann::embA > ::hermann , LIST | info : ::hermann::embA # the class and the class namespace is UNCHANGED print -list ::classB > LIST | ::classB : <namespace> | : variables : * : info : vars | : public-own : classB : ~classB | : private-own : bProc | -------------------- : ------------------------------------
Task: create a LOCAL instance in a GLOBAL namespace
# =========================================================================================== # more on REFERENCE in GLOBAL namespace # An instance always require a class ::myooX::ClassN ::MyClass { proc MyClass { myNs someVal } { namespace upvar $myNs my my set my(val) $someVal-123 } proc getVal {myNs} { namespace upvar $myNs my my set my(val) } } > ::MyClass # By default a INSTANCE created with 'NewN' is always located in the namespace of the class : set gmyNs [::myooX::NewN ::MyClass abc] > ::MyClass::MyClass-1 # The INSTANCE-NS is like the CLASS-NS a namespace with an array variable : print -list $gmyNs ::MyClass > LIST | ::MyClass::MyClass-1 : <namespace> | : variables : * : info : vars | -------------------- : --------------------------------- | ::MyClass : <namespace> | : variables : * : info : vars | : children : ::MyClass::MyClass-1 | : public-own : MyClass | : private-own : getVal | -------------------- : ---------------------------------------- # A INSTANCE-NS can always made to a LOCAL-REF using : namespace upvar $gmyNs my gmy > # The 'gmy' and the 'gmyNs::my' point to the SAME variable : print -list gmy ${gmyNs}::my > LIST | gmy : <instance> | : __CLASS__ : ::MyClass | : __NAME__ : MyClass-1 | : __NS__ : ::MyClass::MyClass-1 | : val : abc-123 | -------------------- : ----------------------------------- | ::MyClass::MyClass-1::my : <instance> | : __CLASS__ : ::MyClass | : __NAME__ : MyClass-1 | : __NS__ : ::MyClass::MyClass-1 | : val : abc-123 | -------------------- : ----------------------------------- # Calling an instance-method is simple : ::MyClass::getVal $gmyNs > abc-123 # or just use the LOCAL-REF : ::MyClass::getVal $gmy(__NS__) > abc-123 # a proc using the instance does NOT require to be part of the class : proc outsider {myNs} { # create LOCAL-REF 'my' namespace upvar $myNs my my # access 'my' return "my=$my(val)" } > # call a proc using the INSTANCE-NS always works outsider $gmyNs > my=abc-123 # call a proc using LOCAL-REF works with '__NS__' outsider $gmy(__NS__) > my=abc-123 # call a proc using STRING-NS always works outsider "::MyClass::MyClass-1" > my=abc-123
variable
because of 'special' usage.upvar
Task: create a LOCAL instance in a LOCAL namespace
# =========================================================================================== # more on REFERENCE in LOCAL namespace # An instance always require a class ::myooX::ClassN ::MyClass { proc MyClass { myNs someVal } { namespace upvar $myNs my my set my(val) $someVal-123 } proc getVal { myNs } { namespace upvar $myNs my my set my(val) } } > ::MyClass # test INSTANCE access in the namespace '::dummy' : namespace eval ::dummy { namespace current > ::dummy variable dmyNs > # create a INSTANCE in the namespace '::dummy' : # REMEMBER: 'Create3N CLASS NAME NS ...' is the same as 'Class2N CLASS NS::NAME ...' set dmyNs [::myooX::Create3N ::MyClass xyz ::dummy otto] > ::dummy::xyz # same as above but using '::dummy::xyz' # ATTENTION: overwrite an EXISTING instance will NOT harm and just call the CTOR and return the EXISTING inssance : set dmyNs [::myooX::Create2N ::MyClass ::dummy::xyz otto] > ::dummy::xyz # REMEMBER: the DEFAULT namespace is always the CLASS namespace and NOT the CURRENT namespace : set dmy2Ref [::myooX::Create2N ::MyClass abc otto] > ::MyClass::abc # The INSTANCE-NS is like the CLASS-NS a namespace with an array variable : print -list ::dummy $dmyNs ::MyClass > LIST | ::dummy : <namespace> | : variables : * : info : vars | : children : ::dummy::xyz | -------------------- : --------------------------------- | ::dummy::xyz : <namespace> | : variables : * : info : vars | -------------------- : --------------------------------- | ::MyClass : <namespace> | : variables : * : info : vars | : children : ::MyClass::abc | : public-own : MyClass | : private-own : getVal | -------------------- : ----------------------------------- # A INSTANCE-NS can always made to a LOCAL-REF using : namespace upvar $dmyNs my dmyR > # Is 'dmyR' created in the LOCAL-NS ? namespace which -variable dmyR > ::dummy::dmyR # print NS of LOCAL-REF print dmyR(__NS__) > dmyR(__NS__)<::dummy::xyz> # The 'dmyR' and the 'dmyNs::my' point to the SAME variable : print -list dmyR ${dmyNs}::my > LIST | dmyR : <instance> | : __CLASS__ : ::MyClass | : __NAME__ : xyz | : __NS__ : ::dummy::xyz | : val : otto-123 | -------------------- : --------------------------- | ::dummy::xyz::my : <instance> | : __CLASS__ : ::MyClass | : __NAME__ : xyz | : __NS__ : ::dummy::xyz | : val : otto-123 | -------------------- : --------------------------- # Calling an instance-method is simple : ::MyClass::getVal $dmyNs > otto-123 # or just use the LOCAL-REF : ::MyClass::getVal $dmyR(__NS__) > otto-123 # a proc using the instance does NOT require to be part of the class : proc outsider {dmyNs} { # create LOCAL-REF 'my' namespace upvar $dmyNs my my # access 'my' return "my=$my(val)" } > # call a proc using the INSTANCE-NS always works outsider $dmyNs > my=otto-123 # call a proc using LOCAL-REF works with '__NS__' outsider $dmyR(__NS__) > my=otto-123 # call a proc using STRING-NS always works outsider "::dummy::xyz" > my=otto-123 }
variable
because of 'special' usage.upvar
I struggled with whether myoo should be array-reference-based or namespace-reference-based.
After doing some research with the tclmyoo TCL-API and libmyoo C-API I switch to namespace-reference-based
The following reasons led to the switch to namespace-reference :
TclGetNamespaceFromObj
from the tcl internal Int API, a very powerful tool is available for resolving a namespace
.namespace upvar NS my my
is significantly faster than upvar NS::my my
.::
).Tcl_Namespace*
pointer, which speeds up the All-Language-Compiler (ALC) compiler massively.This section compare the api from myoo (tclmyoo and libmyoo) with the api from tcloo.
It should be noted that myoo, in contrast to tcloo, uses an array
as a data structure and is therefore 100%-TCL-compilant.
A possible extension would be a direct mapping of the class and instance as a C data structure based on the tcl-array-code, but this will not be 100%-TCL-compliant.