Tcl OO method 'new' failing to override - tcl

Why does the 'new' method not seem to allow overriding in Tcl OO?
% oo::class create A {
method new {} { puts "In A's new"; return [next] }
}
::A
% A new
::oo::Obj15
% A new
::oo::Obj16
% info class definition A new
{} { puts "In A's new"; return [next] }
% info class call A new
{method new ::A method}
A's new method exists, but doesn't seem to get called when "A new" is executed.
Can anyone explain what's happening here?

The methods you define in a class can be invoked on an instance of the class:
% oo::class create A {
method new {} { puts "In A's new"; return [next] }
}
::A
% set obj [A new]
::oo::Obj12
% $obj new
In A's new
no next method implementation
If you want to augment the new method of class A, use self:
% oo::class create A {
self method new {} { puts "In A's new"; return [next] }
}
::A
% A new
In A's new
::oo::Obj12

As documented at http://www.tcl-lang.org/man/tcl8.6/TclCmd/class.htm#M10, MyClass new calls the class constructor not a method named "new".
You want
oo::class create A {
constructor {} { puts "In A's new"; return [next] }
}

When you do $obj $mth, the $mth method is looked up on the $obj object, with all its contributions from the class hierarchy. In the case of A new, the object is the class A, and the method is called new, that has an implementation supplied by oo::class (which makes the object structures, allocates a name, calls the constructors with the arguments you gave, and returns the fully-qualified name).
You can intercept that by defining a new method on A itself, or by mixing in a suitable class into the object, or by using a metaclass. (All of those will want to use next to delegate actual construction to the standard method.)
Scheme 1: object method on class object
oo::class create A {
self method new {} { puts "In A's new"; return [next] }
}
# Variant of scheme 1; syntactically different, makes same thing
oo::class create A
oo::objdefine A {
method new {} { puts "In A's new"; return [next] }
}
Scheme 2: mixin into class object
oo::class create B {
method new {} { puts "In [self]'s new"; return [next] }
}
oo::class create A {
self mixin B
}
Scheme 3: metaclass
(Metaclasses are just classes that subclass oo::class itself.)
oo::class create C {
superclass oo::class
method new {} { puts "In [self]'s new"; return [next] }
}
C create A {
# any other method definitions you want here
}
Metaclasses are definitely the best option when you want to make whole families of these things, and mixins allow for a more targeted approach. (Yes, the capabilities overlap.)

Related

Self refence within class methods in Itcl

Is it possible to refer to current object within the member method of a class.
Kindly consider the code below:
itcl::class widget {
private variable inst
method ChildCount {} {
return [llength [keylkeys inst children]]
}
method AddChild {childWidget} {
inst children.[ChildCount] $childWidget
# ***HOW TO GET THIS WORKING?***
$childWidget SetParent $self
}
}
What could be the equivalent for $self in $childWidget SetParent $self?
Ok, so evident after little more online search -> $this variable should do the the trick.

TclOO Variable Scope with Inheritance/superclass

I stumbled over the variable scope when inheriting a class with TclOO.
The member variable nCrumbs below is not visible to the inherited class without repeating the declaration.
Is there a way to avoid replicating all the variable declarations from the superclass?
(I read through all the OO documentation, specifically oo::define and oo::object, also the non-exported stuff, googled for it. There are so many concepts to get around various things, I got lost.
I am looking for something that keeps the inherited class as simple as possible. The superclass may have any fancy complicated code in it, though.)
Any help would be greatly appreciated, thanks.
oo::class create toaster {
variable nCrumbs; #declaration
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
}
}
oo::class create smartToaster {
superclass toaster; #inherit
variable nCrumbs; #<======= have to declare again
method toast {nSlices} {
if {$nCrumbs > 40} {
my clean
}
next $nSlices; #call superclass method
}
}
set clsToaster [smartToaster new]
$clsToaster toast 2
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

When redirecting to another constructor in Dart, is it required to provide all the arguments?

So, in Ruby, if I call super without arguments, it automatically passes all the arguments from the current method to super. Is this possible to do in Dart?
Example how it's done:
class SomeClass {
SomeClass(arg1, arg2) {}
SomeClass.build(arg1, arg2) : super(arg1, arg2);
}
Could it be done like this:
class SomeClass {
SomeClass(arg1, arg2) {}
SomeClass.build : super;
}
or in some similar way?
No, you have to provide all the arguments.

calling super() from an actionscript constructor with varargs

If a constructor takes its parameters as a vararg (...) it seems to be impossible to create a subclass that will just pass on that vararg to the superclass.
There is a related question with fix for this same situation for normal functions: Wrapping a Vararg Method in ActionScipt but I cannot get that to work with a super call.
base class:
public class Bla
{
public function Bla(...rest)
{
trace(rest[0]); // trace the first parameter
}
}
subclass:
public class Blie extends Bla
{
public function Blie(...rest)
{
// this is not working, it will
// pass an array containing all
// parameters as the first parameters
super(rest);
}
}
if I now call
var b1 = new Bla('d', 'e');
var b2 = new Blie('a', 'b', 'c');
I get the output
d
a,b,c
And I want it to print out:
d
a
Aside from actually moving the handling of the parameters to the subclass or shifting it off to a separate initializer method, does anyone know how to get the super call right?
There's unfortunately no way to call the super constructor with ... args. If you remove the super() call, it will be called by the compiler (with no arguments). arguments is also not accessible from constructors.
If you can change the method signatures, you modify the arguments to accept an Array rather than ... args. Otherwise, as you mentioned, you could move it into an initializer method.
You may use a statement like this:
override public function doSomething(arg1:Object, ...args):void {
switch(args.length) {
case 0: super.doSomething(arg1); return;
case 1: super.doSomething(arg1, args[0]); return;
case 2: super.doSomething(arg1, args[0], args[1]); return;
}
}

ITCL - How to access associative array member inside a class?

How to access associative array member of a class inside the class itself? Itcl is modeled after C++, and in C++ we would write:
SomeObject.SomePublicMember = ...
How to do the same in Itcl? Without providing accessor procedure for such an array. I've seen that for usual plain variables this can be obtained by using cget:
$this cget -PublicMemberVariableName
However the following construct doesn't work:
$this cget -AssociativeArrayName(NamedIndex)
Is this possible at all?
Alas, cget won't get what you want. The array element isn't passed all the way down to ItclGetInstanceVar (I'm not sure why).
You can use get/set and the like:
class myObject {
public variable AssArray
constructor {} {
array set AssArray ""
}
method setArr { elem val } {
set AssArray($elem) $val
}
method getArr { elem } {
return $AssArray($elem)
}
method getFullArr {} {
return [array names AssArray]
}