A simple comparison of Tcl object extensions

In January of 2004, a poster to comp.lang.tcl asked which of the tcl extensions providing object orientation allowed one to define class members which were other objects.

Several people submitted answers - and some of the answers provided demonstrated how the different extensions might do just that.

Below, examples for Snit, itcl, stooop (moodss related), XOtcl and TclOO are provided.

Are there other popular Tcl object oriented extensions to represent?


Snit

 snit::type C2 {
    variable this
    variable that

    constructor {args} {
        set this [C2 %AUTO%]
        set that [C2 %AUTO%]
    }

    destructor {
        catch {$this destroy}
        catch {$that destroy}
    }

    method doThis {} {
        $this doSomething
    }

    method doThat {} {
        $that doSomething
    }
 }

Shouldn't it be like this:

 snit::type C1 {
    option -partof
    method doSomething {} {
        puts "[$self cget -partof]'s $self doin' it"
    }
 }
 
 snit::type C2 {
    delegate method doThis to this as doSomething
    delegate method doThat to that as doSomething

    constructor {args} {
        install this using C1 %AUTO% -partof $self
        install that using C1 %AUTO% -partof $self
        $self configurelist $args
    }

    destructor {
        catch {$this destroy}
        catch {$that destroy}
    }
 }

incr Tcl

  itcl::class C1 {
  }

  itcl::class C2 {
      variable m1
      variable m2
      constructor {} {
          set m1 [C1 #auto]
          set m2 [C1 #auto]
      }
      destructor {
          itcl::delete object $m1 $m2
      }
  }

stooop

 class C2 {
     proc C2 {this} {
         set ($this,o1) [new C1]
         set ($this,o2) [new C1]
     }
     proc ~C2 {this} {
         delete $($this,o1) $($this,o2)
     }
     class C1 {
         proc C1 {this} {}
         proc ~C1 {this} {}
     }
 }
 delete [new C2]

Note: in the example above, C1 is a class embedded in C2, but might as well be defined outside of C2, as in:

 class C1 {
     proc C1 {this} {}
     proc ~C1 {this} {}
 }
 class C2 {
     proc C2 {this} {
         set ($this,o1) [new C1]
         set ($this,o2) [new C1]
     }
     proc ~C2 {this} {
         delete $($this,o1) $($this,o2)
     }
 }

XOTcl

There are some posibilities

Example 1 (generate global objects in the constructor and refer to it via instance variables)

   Class C1
   Class C2
   C2 instproc init {} {
       my instvar o1 o2
       set o1 [C1 new]
       set o2 [C1 new]
   }
   C2 instproc destroy {} {
       my instvar o1 o2
       $o1 destroy
       $o2 destroy 
       next
   }

Example 2 (by using anonymous nested objects. Does not need to destroy explicit the sub-objects)

Sub-objects are better to implement aggregation or membership (part of, has a)

   Class C1
   Class C2 -parameter {o1 o2}
   C2 instproc init {} {
       my o1 [C1 new -childof [self]]
       my o2 [C1 new -childof [self]]
   }

Example 3 (by using named sub-objects. Does not need to destroy explicit the sub-objects)

Quite similar to C++ object members

   Class C1
   Class C2
   C2 instproc init {} {
       C1 create [self]::o1
       C1 create [self]::o2  
   }

Example 4 (by using anonymous sub-objects generated via parameters. Sub-objects are destroyed automatically when container is deleted)

Quite similar to example 2, but no need for a explicit constructor

    ::xotcl::Class::Parameter C1
    Class C2 -parameter {
      {o1 -Class C1 -default 1}
      {o2 -Class C1 -default 1}
    }

TclOO

(By DKF on 22-Jan-2011)

  oo::class create C1

  oo::class create C2 {
      variable m1 m2
      constructor {} {
          set m1 [C1 new]
          set m2 [C1 new]
      }
      destructor {
          $m1 destroy
          $m2 destroy
      }
  }

Or alternatively:

  oo::class create C1

  oo::class create C2 {
      variable m1 m2
      constructor {} {
          set m1 [C1 create m1]
          set m2 [C1 create m2]
      }
  }

Note that in the latter example, the full name of m1 is [info object namespace $c2inst]::m1; this means it will be automatically destroyed when the outer container ($c2inst) is destroyed due to the deletion of the instance namespace. This is tricky (and is a trick used by TDBC to manage its cleanup). Also note that inside the methods of C2, the subobjects can be referred to as m1 and m2 because the names match the variable names. However the local names are formally arbitrary; all that's needed is for them to be in the right namespace (and it's probably a bad idea to overwrite or override any existing Tcl command).