Version 1 of Reference counted command objects

Updated 2002-03-14 02:18:28

I realized the mechanism tcom uses to implement handles might be the basis for a more general reference counted object implementation. I threw together a quick and dirty proof of concept and put it at the http://www.vex.net/~cthuang/counted/ Web site. This is an extension which allows you to create a handle to an incr Tcl object. The handle represents a reference counted object and when the last reference is released the incr Tcl object is destroyed.

In the following example, the command

    ::counted::command $account [list delete object $account]

returns a handle to a command object which delegates its operations to the incr Tcl object specified by $account. The second argument to the ::counted::command command specifies the script to execute when the last reference to the object is released. In this case, it deletes the $account incr Tcl object.

I added the ::counted::type command so I can show the object survives when the handle's internal representation shimmers to a string. The ::counted::type command returns the name of the internal representation type of its argument.

    package require counted

    package require Itcl
    namespace import itcl::*

    class Account {
        public variable balance 0

        destructor {
            puts "Account::destructor"
        }

        public method deposit {amount} {
            set balance [expr $balance + $amount]
        }

        public method withdraw {amount} {
            set balance [expr $balance - $amount]
        }
    }

    proc createAccount {} {
        set account [Account #auto]

        # Create a reference counted object that delegates operations to an Itcl
        # object.  When the last reference is released, destroy the Itcl object.
        return [::counted::command $account [list delete object $account]]
    }

    set accountObj [createAccount]

    # The internal representation type is "cmdName".
    puts "Internal representation type is [::counted::type $accountObj]"
    puts "balance is [$accountObj cget -balance]"
    $accountObj deposit 30
    puts "balance is [$accountObj cget -balance]"

    # This command changes the internal representation type to "string".
    puts "$accountObj length is [string length $accountObj]"
    puts "Internal representation type is [::counted::type $accountObj]"

    # This command restores the internal representation type to "cmdName".
    $accountObj withdraw 20
    puts "Internal representation type is [::counted::type $accountObj]"
    puts "balance is [$accountObj cget -balance]"

    # Release the last reference.
    unset accountObj

Here's another example that implements Lambda in Tcl. Like Feather LambdaObj, the command object is automatically destroyed when the last reference is released.

    package require counted

    namespace eval lambda {variable unique 0}

    proc lambda {arguments body} {
        set procName ::lambda::cmd[incr ::lambda::unique]
        proc $procName $arguments $body
        return [::counted::command $procName [list rename $procName {}]]
    }

    set add [lambda {a b} {puts "$a + $b = [expr {$a + $b}]"}]
    puts [info procs ::lambda::*]
    $add 1 2
    unset add
    puts [info procs ::lambda::*]

The output of this script is

    ::lambda::cmd1
    1 + 2 = 3