Home

Sponsors

Download

Install

Running

Examples

Tcl Scripts

News

Future Work

Copyright

Setting up your Tcl Interpreter to use CABLE-generated Wrappers

Generated Tcl wrapper libraries do not run by themselves. They require a Tcl package called "cable" which is provided by CABLE. This package not only provides some convenient commands, but is also in charge of coordinating the interaction of multiple wrapper packages. All CABLE-generated wrapper packages tell the interpreter that they require the cable package when they load.

Generated Tcl wrapper packages link to the "CableTclFacility" library which provides the "cable" Tcl package. Loading a generated package will automatically find and load the "cable" package. However, if you wish to write code like "package require cable", then your TCLLIBPATH must be set to tell Tcl how to find the cable package. If you are using an installation of CABLE, set TCLLIBPATH to include the full path to the "PREFIX/share/Cable" directory of the installation. If you are using CABLE directly from its build directory, set TCLLIBPATH to include the full path to the "CableTclFacility" directory within the BUILD directory (not the source).

DO NOT attempt to use both an installed CABLE and a copy from the build directory. The CableTclFacility shared library is loaded by its full path as a package. Wrapper packages also link to the CableTclFacility shared library when they build, so it is loaded as part of opening the wrapper package's library. If two copies of the library exist, even identical copies, random segmentation faults can occur.

Using the CABLE-generated Wrappers

In order to write Tcl scripts that use wrapped C++ classes, you will have to be familiar with the syntax of creating objects and invoking methods from Tcl. Most of the language semantics are close approximations to the behavior in C++. This includes automatic object destruction, overload resolution, and reference and pointer semantics. For the reference by the examples included in the instructions below, consider this simple C++ class:
  class Integer
  {
  public:
    Integer(): m_Int(0) {}
    Integer(const Integer& r): m_Int(r.m_Int)  {}
    Integer(int i): m_Int(i) {}
    ~Integer() {}
    int Get() const { return m_Int; }
    void Set(int i) { m_Int = i; }
    const Integer* GetPointer() const { return this; }
    Integer& operator+=(const Integer& r)
      { m_Int += r.m_Int; return *this; }
    static float Mean(const Integer& l, const Integer& r)
      { return (float(l.m_Int)+float(r.m_Int))/2.0; }
  private:
    int m_Int;
  };
    

Constructors

Unlike other object-oriented systems in Tcl, instances of wrapped classes do not have names. Instead, they are associated with Tcl objects, which can be referenced by Tcl variables, lists, arrays, and other data structures. Any wrapped class that has public constructors available provides a Tcl command with the name of the class. Invoking this command will create an instance of the class by calling a constructor selected by C++ overload resolution:
  class-name arg1 arg2 ...
    
The command will return an instance of the class as the Tcl result. You can store a reference to this object in a Tcl variable using the "set" command. For example, we can create instances of the Integer class like this:
  # Create an Integer object with value 0.
  set x1 [Integer]

  # Create an Integer object copied from $x1.
  set x2 [Integer $x1]

  # Create an Integer object with value 578.
  set x3 [Integer 578]
    
An important note is that Tcl variables act as references to the objects. After creating the above objects, the command "set x2b $x2" will NOT create a copy of the Integer object. It simply adds another Tcl reference to it. To create a copy, you must explicitly call the copy constructor, as in the example above to copy $x1.

Destructors

When Tcl loses all references to an object, its destructor is automatically called:
  # Delete an Integer object created in the example above.
  set x3 {}
  # The Integer with value 578 has been destroyed.
    
This will also work for local variables in Tcl procedures:
  proc Foo {} {
    # Create an Integer object.
    set temp [Integer 5]
    return 0
    # The Tcl variable "temp" goes out of scope,
    # so the Integer object is destroyed.
  }
    

Methods

Once you have an instance of a class, you can invoke methods on it by using the object itself as a command. The first argument to the command is the name of the method you want to invoke. Additional arguments will be passed to the method when it is called, and are used in overload resolution:
  object method-name arg1 arg2 ...
    
The result of the command will be the value returned by the method. This value can be stored in a Tcl variable, ignored, or passed directly into another method invocation. As an example, we can invoke some methods on the Integer objects created in the above example:
  # Set the first Integer to value 6.
  $x1 Set 6

  # Print out the value of the second integer.
  puts [$x2 Get]

  # Set the value of the second integer to that of the first.
  $x2 Set [$x1 Get]
    
Some methods may return C++ pointers or references to objects. If the classes of these objects have been wrapped, you can call methods directly through the pointer or reference, with no special syntax. For example, if we call GetPointer on an Integer object, we will get a pointer of type "const Integer*". We can save this pointer and use it to invoke methods. However, const correctness is enforced, so in this example, we will only be able to invoke const methods:
  # Get a pointer to the Integer referenced by x1.
  set p1 [$x1 GetPointer]
  
  # Call the "Get" method, which is const.
  puts [$p1 Get]
  
  # Try to call the "Set" method.  This should
  # produce an error.
  $p1 Set 3
    

Static Methods

Just as in C++, static methods can be invoked with or without an instance of the class. Invoking the methods with an instance works just like any other method:
  object static-method-name arg1 arg2 ...
    
Static methods can also be invoked through just the name of the class:
  class-name static-method-name arg1 arg2 ...
    
For example, we can call the "Mean" method to find the average value of two Integer objects created above:
  set mean [Integer Mean $x1 $x2]
  puts "The mean value is $mean"
    

Operators

Any operator that is defined as a member of a class can be invoked by its name using the same syntax as calling a method. For example, we can add one Integer object to another like this:
    $x1 += $x2
    puts "Sum = [$x1 Get]"
    

Namespace-level Functions and Operators

Functions and operators not defined in a class scope are currently NOT supported. There are no known technical difficulties with adding the support, but grouping the functions and operators into packages requires more complicated configuration files. This functionality has not yet been implemented.

Commands Provided by the cable Package

The cable package provides some commands to aid script writers in working with CABLE-generated wrappers.

cable::ListMethods <object>

Returns a string listing methods that can be invoked on the object. The methods are displayed as C++ prototypes, and are grouped by the class in which they are defined.

cable::TypeOf <object>

Returns the type of the object as it will be seen when passing it as an argument to a method.

cable::Interpreter

This command returns a pointer to the Tcl interpreter in which the command was invoked. The result is a value of type "Tcl_Interp*" that can be passed to methods that know about Tcl interpreters.

cable::Boolean <object>

This command accepts a wrapped C++ entity as an argument. It will attempt to convert the argument to a boolean result using C++ rules for implicit conversion-to-bool. This can be used to detect whether a pointer is NULL.

cable::DebugOn and cable::DebugOff

These commands are used to turn on and off verbose debugging output of the wrappers' internal manipulation of objects. These are used to help debug CABLE generators and the cable package itself.