Auto Loading Itcl Class Methods in Other File

Viewed 123

I am recently attempting to upgrade to TCL 8.6 (Yeah, several years late) from 8.5. I am running into an issue auto loading our itcl classes. In a number of our classes, we store the class methods in different files from the file with the class definition. Auto load was happy to handle this. In TCL 8.6, itcl is running into an issue auto loading the class methods. The class definition is autoloaded correctly, though. Is it still possible to have itcl autoload the class methods outside of the class definition file? If I call auto_load directly, the method will be loaded.

Note that for the example below, I did hand create the tclIndex file and it is not stored in auto_path.

testClass Definition File testClass.tcl

itcl::class testClass {

    constructor {args} {}
    destructor  {args}

    public    method foo  {}
}
itcl::body testClass::constructor {args} {
    puts "Created testClass"
}
itcl::body testClass::destructor {args} {
}

testClass Foo method Definition File testClassFoo.tcl

itcl::body testClass::foo {args} {
    puts "Bar"
}

tclIndex File

set dir /opt/tclClassTest
set auto_index(testClass) [list source [file join $dir testClass.tcl]]
set auto_index(::testClass::constructor) [list source [file join $dir testClass.tcl]]
set auto_index(::testClass::destructor) [list source [file join $dir testClass.tcl]]
set auto_index(::testClass::foo) [list source [file join $dir testClassFoo.tcl]]

testCode

% puts $tcl_version
8.6
% package require itcl
4.2.1
% source /opt/tclClassTest/tclIndex
source /opt/tclClassTest/testClassFoo.tcl
% testClass t1
Created testClass
t1
% t1 foo
member function "::testClass::foo" is not defined and cannot be autoloaded
% auto_load ::testClass::foo
1
% t1 foo
Bar
% 
1 Answers

The core of the issue is that methods in itcl 3 were basically commands, whereas methods in itcl 4 are categorically not (at the C level, they have a different signature; methods now have an extra argument that describes the invocation context, which describes the object in a way that respects dynamic behaviour). That cleans up a lot of the innards, but does mean that command-based mechanisms (such as autoloading) will not work.

I think the simplest fix is to just source those auxiliary definition files explicitly at the end of your main class file (it really won't take that long under any sane scenario). To read in files relative to the current file, do:

source [file join [file dirname [info script]] otherfile.tcl]

If there's a lot of them, use a loop like this:

apply {args {
    set dir [file dirname [info script]]
    foreach filename $args {
        uplevel 1 [list source [file join $dir $filename]]
    }
}} otherfile1.tcl otherfile2.tcl otherfile3.tcl

(That has the advantage of not polluting the current namespace with variables relating to doing that loop; apply is great for run-once procedures.)

Related