- NAME
- oo::define, oo::objdefine — define and configure classes and objects
- SYNOPSIS
- DESCRIPTION
- CONFIGURING CLASSES
- classmethod name ?argList bodyScrip?
- constructor argList bodyScript
- destructor bodyScript
- export name ?name ...?
- forward name cmdName ?arg ...?
- initialise script
- initialize script
- method name ?option? argList bodyScript
- private cmd arg...
- private script
- self subcommand arg ...
- self script
- self
- superclass ?-slotOperation? ?className ...?
- unexport name ?name ...?
- variable ?-slotOperation? ?name ...?
- ADVANCED CLASS CONFIGURATION OPTIONS
- definitionnamespace ?kind? namespaceName
- deletemethod name ?name ...?
- filter ?-slotOperation? ?methodName ...?
- mixin ?-slotOperation? ?className ...?
- renamemethod fromName toName
- CONFIGURING OBJECTS
- export name ?name ...?
- forward name cmdName ?arg ...?
- method name ?option? argList bodyScript
- mixin ?-slotOperation? ?className ...?
- private cmd arg...
- private script
- unexport name ?name ...?
- variable ?-slotOperation? ?name ...?
- ADVANCED OBJECT CONFIGURATION OPTIONS
- class className
- deletemethod name ?name ...
- filter ?-slotOperation? ?methodName ...?
- renamemethod fromName toName
- self
- PRIVATE METHODS
- SLOTTED DEFINITIONS
- slot -append ?member ...?
- slot -clear
- slot -prepend ?member ...?
- slot -remove ?member ...?
- slot -set ?member ...?
- SLOT IMPLEMENTATION
- slot Get
- slot Resolve slotElement
- slot Set elementList
- EXAMPLES
- SEE ALSO
- KEYWORDS
oo::define, oo::objdefine — define and configure classes and objects
package require tcl::oo
oo::define class defScript
oo::define class subcommand arg ?arg ...?
oo::objdefine object defScript
oo::objdefine object subcommand arg ?arg ...?
The oo::define command is used to control the configuration of classes,
and the oo::objdefine command is used to control the configuration of
objects (including classes as instance objects), with the configuration being
applied to the entity named in the class or the object argument.
Configuring a class also updates the
configuration of all subclasses of the class and all objects that are
instances of that class or which mix it in (as modified by any per-instance
configuration). The way in which the configuration is done is controlled by
either the defScript argument or by the subcommand and following
arg arguments; when the second is present, it is exactly as if all the
arguments from subcommand onwards are made into a list and that list is
used as the defScript argument.
Note that the constructor for oo::class will call oo::define on
the script argument that it is provided. This is a convenient way to create
and define a class in one step.
The following commands are supported in the defScript for
oo::define, each of which may also be used in the subcommand form:
- classmethod name ?argList bodyScrip?
-
This creates a class method, or (if argList and bodyScript are
omitted) promotes an existing method on the class object to be a class
method. The name, argList and bodyScript arguments are as in
the method definition, below.
Class methods can be called on either the class itself or on the instances of
that class. When they are called, the current object (see the sel and
my commands) is the class on which they are called or the class of the
instance on which they are called, depending on whether they are called on the
class or an instance of the class, respectively. If called on a subclass or
instance of the subclass, the current object is the subclass.
In a private definition context, the methods as invoked on classes are
not private, but the methods as invoked on instances of classes are
private.
- constructor argList bodyScript
-
This creates or updates the constructor for a class. The formal arguments to
the constructor (defined using the same format as for the Tcl proc
command) will be argList, and the body of the constructor will be
bodyScript. When the body of the constructor is evaluated, the current
namespace of the constructor will be a namespace that is unique to the object
being constructed. Within the constructor, the next command should be
used to call the superclasses' constructors. If bodyScript is the empty
string, the constructor will be deleted.
Classes do not need to have a constructor defined. If none is specified, the
superclass's constructor will be used instead.
- destructor bodyScript
-
This creates or updates the destructor for a class. Destructors take no
arguments, and the body of the destructor will be bodyScript. The
destructor is called when objects of the class are deleted, and when called
will have the object's unique namespace as the current namespace. Destructors
should use the next command to call the superclasses' destructors. Note
that destructors are not called in all situations (e.g. if the interpreter is
destroyed). If bodyScript is the empty string, the destructor will be
deleted.
-
Note that errors during the evaluation of a destructor are not returned
to the code that causes the destruction of an object. Instead, they are passed
to the currently-defined bgerror handler.
- export name ?name ...?
-
This arranges for each of the named methods, name, to be exported
(i.e. usable outside an instance through the instance object's command) by the
class being defined. Note that the methods themselves may be actually defined
by a superclass; subclass exports override superclass visibility, and may in
turn be overridden by instances.
- forward name cmdName ?arg ...?
-
This creates or updates a forwarded method called name. The method is
defined be forwarded to the command called cmdName, with additional
arguments, arg etc., added before those arguments specified by the
caller of the method. The cmdName will always be resolved using the
rules of the invoking objects' namespaces, i.e., when cmdName is not
fully-qualified, the command will be searched for in each object's namespace,
using the instances' namespace's path, or by looking in the global namespace.
The method will be exported if name starts with a lower-case letter, and
non-exported otherwise.
If in a private definition context (see the private definition command,
below), this command creates private forwarded methods.
- initialise script
-
- initialize script
-
This evaluates script in a context which supports local variables and
where the current namespace is the instance namespace of the class object
itself. This is useful for setting up, e.g., class-scoped variables.
- method name ?option? argList bodyScript
-
This creates or updates a method that is implemented as a procedure-like
script. The name of the method is name, the formal arguments to the
method (defined using the same format as for the Tcl proc command) will
be argList, and the body of the method will be bodyScript. When
the body of the method is evaluated, the current namespace of the method will
be a namespace that is unique to the current object. The method will be
exported if name starts with a lower-case letter, and non-exported
otherwise; this behavior can be overridden via export and
unexport
or by specifying -export, -private or -unexport in the
optional parameter option.
If in a private definition context (see the private definition command,
below) or if the -private flag is given for option, this command
creates private procedure-like methods.
- private cmd arg...
-
- private script
-
This evaluates the script (or the list of command and arguments given by
cmd and args) in a context where the definitions made on the
current class will be private definitions.
The following class definition commands are affected by private:
forward, method, self, and variable. Nesting
private inside private has no cumulative effect; the innermost
definition context is just a private definition context. All other definition
commands have no difference in behavior when used in a private definition
context.
- self subcommand arg ...
-
- self script
-
- self
-
This command is equivalent to calling oo::objdefine on the class being
defined (see CONFIGURING OBJECTS below for a description of the
supported values of subcommand). It follows the same general pattern of
argument handling as the oo::define and oo::objdefine commands,
and
“oo::define cls self subcommand ...”
operates identically to
“oo::objdefine cls subcommand ...”.
If no arguments at all are used, this gives the name of the class currently
being configured.
If in a private definition context (see the private definition command,
below), the definitions on the class object will also be made in a private
definition context.
- superclass ?-slotOperation? ?className ...?
-
This slot (see SLOTTED DEFINITIONS below)
allows the alteration of the superclasses of the class being defined.
Each className argument names one class that is to be a superclass of
the defined class. Note that objects must not be changed from being classes to
being non-classes or vice-versa, that an empty parent class is equivalent to
oo::object, and that the parent classes of oo::object and
oo::class may not be modified.
By default, this slot works by replacement.
- unexport name ?name ...?
-
This arranges for each of the named methods, name, to be not exported
(i.e. not usable outside the instance through the instance object's command,
but instead just through the my command visible in each object's
context) by the class being defined. Note that the methods themselves may be
actually defined by a superclass; subclass unexports override superclass
visibility, and may be overridden by instance unexports.
- variable ?-slotOperation? ?name ...?
-
This slot (see SLOTTED DEFINITIONS below) arranges for each of the named
variables to be automatically made
available in the methods, constructor and destructor declared by the class
being defined. Each variable name must not have any namespace
separators and must not look like an array access. All variables will be
actually present in the namespace of the instance object on which the method
is executed. Note
that the variable lists declared by a superclass or subclass are completely
disjoint, as are variable lists declared by instances; the list of variable
names is just for methods (and constructors and destructors) declared by this
class. By default, this slot works by appending.
If in a private definition context (see the private definition command,
below), this slot manipulates the list of private variable bindings for this
class. In a private variable binding, the name of the variable within the
instance object is different to the name given in the definition; the name
used in the definition is the name that you use to access the variable within
the methods of this class, and the name of the variable in the instance
namespace has a unique prefix that makes accidental use from other classes
extremely unlikely.
The following definitions are also supported, but are not required in simple
programs:
- definitionnamespace ?kind? namespaceName
-
This allows control over what namespace will be used by the oo::define
and oo::objdefine commands to look up the definition commands they
use. When any object has a definition operation applied to it, the class that
it is an instance of (and its superclasses and mixins) is consulted for
what definition namespace to use. oo::define gets the class definition
namespace, and ::oo::objdefine gets the instance definition namespace,
but both otherwise use the identical lookup operation.
This sets the definition namespace of kind kind provided by the current
class to namespaceName. The namespaceName must refer to a
currently existing namespace, or must be the empty string (to stop the current
class from having such a namespace connected). The kind, if supplied,
must be either -class (the default) or -instance to specify the
whether the namespace for use with oo::define or oo::objdefine
respectively is being set.
The class oo::object has its instance namespace locked to
::oo::objdefine, and the class oo::class has its class namespace
locked to ::oo::define. A consequence of this is that effective use of
this feature for classes requires the definition of a metaclass.
- deletemethod name ?name ...?
-
This deletes each of the methods called name from a class. The methods
must have previously existed in that class. Does not affect the superclasses
of the class, nor does it affect the subclasses or instances of the class
(except when they have a call chain through the class being modified) or the
class object itself.
- filter ?-slotOperation? ?methodName ...?
-
This slot (see SLOTTED DEFINITIONS below)
sets or updates the list of method names that are used to guard whether
method call to instances of the class may be called and what the method's
results are. Each methodName names a single filtering method (which may
be exposed or not exposed); it is not an error for a non-existent method to be
named since they may be defined by subclasses.
By default, this slot works by appending.
- mixin ?-slotOperation? ?className ...?
-
This slot (see SLOTTED DEFINITIONS below)
sets or updates the list of additional classes that are to be mixed into
all the instances of the class being defined. Each className argument
names a single class that is to be mixed in.
By default, this slot works by replacement.
- renamemethod fromName toName
-
This renames the method called fromName in a class to toName. The
method must have previously existed in the class, and toName must not
previously refer to a method in that class. Does not affect the superclasses
of the class, nor does it affect the subclasses or instances of the class
(except when they have a call chain through the class being modified), or the
class object itself. Does
not change the export status of the method; if it was exported before, it will
be afterwards.
The following commands are supported in the defScript for
oo::objdefine, each of which may also be used in the subcommand
form:
- export name ?name ...?
-
This arranges for each of the named methods, name, to be exported
(i.e. usable outside the object through the object's command) by the object
being defined. Note that the methods themselves may be actually defined by a
class or superclass; object exports override class visibility.
- forward name cmdName ?arg ...?
-
This creates or updates a forwarded object method called name. The
method is defined be forwarded to the command called cmdName, with
additional arguments, arg etc., added before those arguments specified
by the caller of the method. Forwarded methods should be deleted using the
method subcommand. The method will be exported if name starts with
a lower-case letter, and non-exported otherwise.
If in a private definition context (see the private definition command,
below), this command creates private forwarded methods.
- method name ?option? argList bodyScript
-
This creates, updates or deletes an object method. The name of the method is
name, the formal arguments to the method (defined using the same format
as for the Tcl proc command) will be argList, and the body of the
method will be bodyScript. When the body of the method is evaluated, the
current namespace of the method will be a namespace that is unique to the
object. The method will be exported if name starts with a lower-case
letter, and non-exported otherwise;
this can be overridden by specifying -export, -private or
-unexport in the optional parameter option, or via the
export and unexport definitions.
If in a private definition context (see the private definition command,
below) or if the -private flag is given for option, this command
creates private procedure-like methods.
- mixin ?-slotOperation? ?className ...?
-
This slot (see SLOTTED DEFINITIONS below)
sets or updates a per-object list of additional classes that are to be
mixed into the object. Each argument, className, names a single class
that is to be mixed in.
By default, this slot works by replacement.
- private cmd arg...
-
- private script
-
This evaluates the script (or the list of command and arguments given by
cmd and args) in a context where the definitions made on the
current object will be private definitions.
The following class definition commands are affected by private:
forward, method, and variable. Nesting private inside
private has no cumulative effect; the innermost definition context is
just a private definition context. All other definition commands have no
difference in behavior when used in a private definition context.
- unexport name ?name ...?
-
This arranges for each of the named methods, name, to be not exported
(i.e. not usable outside the object through the object's command, but instead
just through the my command visible in the object's context) by the
object being defined. Note that the methods themselves may be actually defined
by a class; instance unexports override class visibility.
- variable ?-slotOperation? ?name ...?
-
This slot (see SLOTTED DEFINITIONS below) arranges for each of the named
variables to be automatically made available in the methods declared by the
object being defined. Each variable name must not have any namespace
separators and must not look like an array access. All variables will be
actually present in the namespace of the object on which the method is
executed. Note that the
variable lists declared by the classes and mixins of which the object is an
instance are completely disjoint; the list of variable names is just for
methods declared by this object. By default, this slot works by appending.
If in a private definition context (see the private definition command,
below), this slot manipulates the list of private variable bindings for this
object. In a private variable binding, the name of the variable within the
instance object is different to the name given in the definition; the name
used in the definition is the name that you use to access the variable within
the methods of this instance object, and the name of the variable in the
instance namespace has a unique prefix that makes accidental use from
superclass methods extremely unlikely.
The following definitions are also supported, but are not required in simple
programs:
- class className
-
This allows the class of an object to be changed after creation. Note that the
class's constructors are not called when this is done, and so the object may
well be in an inconsistent state unless additional configuration work is done.
- deletemethod name ?name ...
-
This deletes each of the methods called name from an object. The methods
must have previously existed in that object (e.g., because it was created
through oo::objdefine method). Does not affect the classes that the
object is an instance of, or remove the exposure of those class-provided
methods in the instance of that class.
- filter ?-slotOperation? ?methodName ...?
-
This slot (see SLOTTED DEFINITIONS below)
sets or updates the list of method names that are used to guard whether a
method call to the object may be called and what the method's results are.
Each methodName names a single filtering method (which may be exposed or
not exposed); it is not an error for a non-existent method to be named. Note
that the actual list of filters also depends on the filters set upon any
classes that the object is an instance of.
By default, this slot works by appending.
- renamemethod fromName toName
-
This renames the method called fromName in an object to toName.
The method must have previously existed in the object, and toName must
not previously refer to a method in that object. Does not affect the classes
that the object is an instance of and cannot rename in an instance object the
methods provided by those classes (though a oo::objdefine forwarded
method may provide an equivalent capability). Does not change the export
status of the method; if it was exported before, it will be afterwards.
- self
-
This gives the name of the object currently being configured.
When a class or instance has a private method, that private method can only be
invoked from within methods of that class or instance. Other callers of the
object's methods cannot invoke private methods, it is as if the private
methods do not exist. However, a private method of a class can be
invoked from the class's methods when those methods are being used on another
instance object; this means that a class can use them to coordinate behaviour
between several instances of itself without interfering with how other
classes (especially either subclasses or superclasses) interact. Private
methods precede all mixed in classes in the method call order (as reported by
self call).
Some of the configurable definitions of a class or object are slotted
definitions. This means that the configuration is implemented by a slot
object, that is an instance of the class oo::Slot, which manages a list
of values (class names, variable names, etc.) that comprises the contents of
the slot. The class defines five operations (as methods) that may be done on
the slot:
- slot -append ?member ...?
-
This appends the given member elements to the slot definition.
- slot -clear
-
This sets the slot definition to the empty list.
- slot -prepend ?member ...?
-
This prepends the given member elements to the slot definition.
- slot -remove ?member ...?
-
This removes the given member elements from the slot definition.
- slot -set ?member ...?
-
This replaces the slot definition with the given member elements.
A consequence of this is that any use of a slot's default operation where the
first member argument begins with a hyphen will be an error. One of the above
operations should be used explicitly in those circumstances.
Internally, slot objects also define a method --default-operation
which is forwarded to the default operation of the slot (thus, for the class
“variable”
slot, this is forwarded to
“my -append”),
and these methods which provide the implementation interface:
- slot Get
-
Returns a list that is the current contents of the slot, but does not modify
the slot. This method must always be called from a stack frame created by a
call to oo::define or oo::objdefine. This method should not
return an error unless it is called from outside a definition context or with
the wrong number of arguments.
The elements of the list should be fully resolved, if that is a meaningful
concept to the slot.
- slot Resolve slotElement
-
Returns slotElement with a resolution operation applied to it, but does
not modify the slot. For slots of simple strings, this is an operation that
does nothing, whereas for slots of classes, this maps a class name to its
fully-qualified class name. This method must always be called from a stack
frame created by a call to oo::define or oo::objdefine. This
method should not return an error unless it is called from outside a
definition context or with the wrong number of arguments; unresolvable
arguments should be returned as is (as not all slot operations strictly
require that values are resolvable to work).
Implementations should not enforce uniqueness and ordering constraints
in this method; that is the responsibility of the Set method.
- slot Set elementList
-
Sets the contents of the slot to the list elementList and returns the
empty string. This method must always be called from a stack frame created by
a call to oo::define or oo::objdefine. This method may return an
error if it rejects the change to the slot contents (e.g., because of invalid
values) as well as if it is called from outside a definition context or with
the wrong number of arguments.
This method may reorder and filter the elements if this is necessary in
order to satisfy the underlying constraints of the slot. (For example, slots
of classes enforce a uniqueness constraint that places each element in the
earliest location in the slot that it can.)
The implementation of these methods is slot-dependent (and responsible for
accessing the correct part of the class or object definition). Slots also have
an unknown method handler to tie all these pieces together, and they hide
their destroy method so that it is not invoked inadvertently. It is
recommended that any user changes to the slot mechanism be restricted to
defining new operations whose names start with a hyphen.
Most slot operations will initially Resolve their argument list, combine
it with the results of the Get method, and then Set the result.
Some operations omit one or both of the first two steps; omitting the third
would result in an idempotent read-only operation (but the standard mechanism
for reading from slots is via info class and info object).
This example demonstrates how to use both forms of the oo::define and
oo::objdefine commands (they work in the same way), as well as
illustrating four of the subcommands of them.
oo::class create c
c create o
oo::define c method foo {} {
puts "world"
}
oo::objdefine o {
method bar {} {
my Foo "hello "
my foo
}
forward Foo ::puts -nonewline
unexport foo
}
o bar → prints "hello world"
o foo → error "unknown method foo"
o Foo Bar → error "unknown method Foo"
oo::objdefine o renamemethod bar lollipop
o lollipop → prints "hello world"
This example shows how additional classes can be mixed into an object. It also
shows how mixin is a slot that supports appending:
oo::object create inst
inst m1 → error "unknown method m1"
inst m2 → error "unknown method m2"
oo::class create A {
method m1 {} {
puts "red brick"
}
}
oo::objdefine inst {
mixin A
}
inst m1 → prints "red brick"
inst m2 → error "unknown method m2"
oo::class create B {
method m2 {} {
puts "blue brick"
}
}
oo::objdefine inst {
mixin -append B
}
inst m1 → prints "red brick"
inst m2 → prints "blue brick"
This example shows how to create and use class variables. It is a class that
counts how many instances of itself have been made.
oo::class create Counted
oo::define Counted {
initialise {
variable count 0
}
variable number
constructor {} {
classvariable count
set number [incr count]
}
method report {} {
classvariable count
puts "This is instance $number of $count"
}
}
set a [Counted new]
set b [Counted new]
$a report
→ This is instance 1 of 2
set c [Counted new]
$b report
→ This is instance 2 of 3
$c report
→ This is instance 3 of 3
This example demonstrates how to use class methods. (Note that the constructor
for oo::class calls oo::define on the class.)
oo::class create DBTable {
classmethod find {description} {
puts "DB: locate row from [self] matching $description"
return [my new]
}
classmethod insert {description} {
puts "DB: create row in [self] matching $description"
return [my new]
}
method update {description} {
puts "DB: update row [self] with $description"
}
method delete {} {
puts "DB: delete row [self]"
my destroy; # Just delete the object, not the DB row
}
}
oo::class create Users {
superclass DBTable
}
oo::class create Groups {
superclass DBTable
}
set u1 [Users insert "username=abc"]
→ DB: create row from ::Users matching username=abc
set u2 [Users insert "username=def"]
→ DB: create row from ::Users matching username=def
$u2 update "group=NULL"
→ DB: update row ::oo::Obj124 with group=NULL
$u1 delete
→ DB: delete row ::oo::Obj123
set g [Group find "groupname=webadmins"]
→ DB: locate row ::Group with groupname=webadmins
$g update "emailaddress=admins"
→ DB: update row ::oo::Obj125 with emailaddress=admins
This example shows how to make a custom definition for a class. Note that it
explicitly includes delegation to the existing definition commands via
namespace path.
namespace eval myDefinitions {
# Delegate to existing definitions where not overridden
namespace path ::oo::define
# A custom type of method
proc exprmethod {name arguments body} {
tailcall method $name $arguments [list expr $body]
}
# A custom way of building a constructor
proc parameters args {
uplevel 1 [list variable {*}$args]
set body [join [lmap a $args {
string map [list VAR $a] {
set [my varname VAR] [expr {double($VAR)}]
}
}] ";"]
tailcall constructor $args $body
}
}
# Bind the namespace into a (very simple) metaclass for use
oo::class create exprclass {
superclass oo::class
definitionnamespace myDefinitions
}
# Use the custom definitions
exprclass create quadratic {
parameters a b c
exprmethod evaluate {x} {
($a * $x**2) + ($b * $x) + $c
}
}
# Showing the resulting class and object in action
quadratic create quad 1 2 3
for {set x 0} {$x <= 4} {incr x} {
puts [format "quad(%d) = %.2f" $x [quad evaluate $x]]
}
→ quad(0) = 3.00
→ quad(1) = 6.00
→ quad(2) = 11.00
→ quad(3) = 18.00
→ quad(4) = 27.00
next, oo::class, oo::object
class, definition, method, object, slot
Copyright © 2007-2018 Donal K. Fellows