mkClasses 1.0 Manual

Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted. The author makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. By use of this software the user agrees to indemnify and hold harmless the author from any claims or liability for loss arising out of such use.
 

 CONTENTS

mkClasses 1.0 - Object orientated programming with Tcl/Tk 8.

Introduction
Commands
Notes
Examples
Installation
Changes
Author

 INTRODUCTION

mkClasses is a collection of new Tcl commands that provides a(nother) way of object oriented programming with TCL. With mkClasses, classes and methods can be defined and objects be created with private and class-common variables. In addition, classes can inherit the functionality of other classes.

This extension has not been designed to look as similar to C++ as possible. Also there is no implementation for private or protected member functions. Though possible, it would have decreased performance.

 COMMANDS

class Class?:Superclass:...? Args Constructor Destructor

Creates a class. If superclasses are specified their members will be automatically inherited. Arguments may be defined like with a regular procedure. The constructor script will be evaluated every time a new object of this class is created, using the specified arguments. The destructor script will be evaluated every time an object of this class is destroyed without any arguments.

For each created class two commands will be created: Assuming the name of the created class is "MyClass" there will be the commands "MyClass" and "~MyClass".

Objects of class MyClass can be created like "MyClass MyObject Args". A new procedure "MyObject" is created and the constructor of MyClass is evaluated with the given arguments for object MyObject. Within the constructor member functions can be called with "MyObject Member Args" or "[this] Member Args" (see below for the this command).

An object is deleted using "~MyClass MyObject". MyObject must be of type MyClass. The destructor of MyClass is evaluated and the procedure "MyObject" is deleted.

~class Class

Destroys a class and all defined member functions of this class. The command will return an error if any objects of this class exist or if the class is used as a superclass.

member Class:Member Args Body

Defines a member function for a given class. Like with a procedure, arguments and a body have to be defined. If a member with the same name has been inherited from a superclass, it is overwritten. Objects of the given class can address their members with the syntax "MyObject Member Args".

Inherited members can always be addressed with "MyObject Superclass:Member Args", regardless if they have been overwritten or not. Since constructor and destructor are also members with the special names "+" and "-", a superclass' constructor or destructor can be called with "MyObject Superclass:+ Args" and "MyObject Superclass:-" resp.

If the member name equals "unknown", this member is automatically called each time an object calls an unknown, i.e. not defined, member function. It is also called, if a member name is ambiguous. The member "unknown" must have exactly two arguments: The first one will contain the name of the unknown member function, the second one all arguments as a list.

~member Class:Member

Destroys a member of a given class. The command will return an error if any objects of the member's class exist or if the class is used as a superclass.

this

The this command represents the current object and can only be used "within an object", i.e. within member functions as well as constructors and destructors. It returns the name of the current object.

my ObjectVar ?NewValue?

Sets or retrieves an object variable. Similar to the standard "set" command, it can be called with the name of a variable to retrieve the value of this variable or to set it to a new value. Object variables are specific to the object which manipulates them. They can be accessed in any member function as well as in the constructor and destructor of the object, but they cannot be retrieved from any other object, even if it is of the same class.

Internally object variables are held in a global array which is of the same name as the object. If the object's name is "MyObject" there is also a global array named "MyObject" holding all object variables, if there are any. Accessing this array directly is in most cases not necessary nor recommended. However, it provides a way to access object variables from the "outside", e.g. if a binding script is evaluated, which is done on global level and therefore outside of any object.

unmy ObjectVar ?ObjectVar ...?

Unsets object variables. Although the command's name seams nonsense, it is a nice equivalent to the standard command "unset". Internally the object variables to be unset are just deleted from the global array with the object's name.

mine ?Pattern?

Returns a list of all currently defined object variables. If a pattern is provided, it will be interpreted due to the "glob" style rules like in the standard command "string match".

ismine ObjectVar

Returns 1 if the given object variable is defined, otherwise 0.

our CommonVar ?NewValue?

Sets or retrieves a class-common variable. Similar to the standard "set" command, it can be called with the name of a variable to retrieve the value of this variable or to set it to a new value. Class-common variables are specific to the class of the object which manipulates them. They can be accessed in any member function as well as in the constructor and destructor of all objects with the same class, but they cannot be retrieved from an object of a different class.

Internally class-common variables are held in a global array which is of the same name as the object's class. If the object's class is "MyClass" there is also a global array named "MyClass" holding any common variables. Accessing this array directly is in most cases not necessary nor recommended. However, it provides a way to access these variables from the "outside", e.g. if a binding script is evaluated, which is done on global level and therefore outside of any object.

unour CommonVar ?CommonVar ...?

Unsets class-common variables. Like with "unmy", it is an equivalent to the standard command "unset". Internally the variables to be unset are just deleted from the global array with the name of the object's class.

ours ?Pattern?

Returns a list of all currently defined class-common variables. If a pattern is provided, it will be interpreted due to the "glob" style rules like in the standard command "string match".

isours CommonVar

Returns 1 if the given class-common variable is defined, otherwise 0.

cinfo Option ?Args?

Provides information about currently defined classes and member functions, as well as of existing objects or eventually inherited classes. Cinfo accepts the following options:

cinfo class Object
Returns the class name of a given object.

cinfo classes
Returns all currently defined classes.

cinfo objects ?Class?
Returns all currently existing objects. If a class is provided, returns only the objects of this class.

cinfo members Class
Returns all currently defined members of a given class. The constructor is represented as "+", the destructor as "-". This allows for retrieving information with the options "body" and "args".

cinfo isclass Class
Returns 1 if the given class exists, 0 otherwise.

cinfo isobject Object
Returns 1 if the given object exists, 0 otherwise.

cinfo ismember Class:Member
Returns 1 if the given member exists, 0 otherwise.

cinfo body Class:Member
Returns the body of the specified member function.

cinfo args Class:Member
Returns the arguments of the specified member function.

cinfo superclasses Class
Returns all superclasses of a given class or an empty string, if there are none.

throw Message ?ErrorInfo? ?ErrorCode?

Raises an exception similar to the standard "error" command. It can only be used within member functions, constructors and destructors, since the object's class and the object's name is inserted in front of the error text prior to printing it out and terminating all further execution. As with the "error" command, an exception raised by "throw" can be caught with "catch".

myincr ObjectVar ?Increment?

Increments an object variable by 1 or by a given increment. This command is similar to the standard "incr" command, but it operates directly on object variables.

myindex ObjectVar Index

Considers the specified object variable to be a list and returns the element indicated by Index. This command is similar to the standard "lindex" command, but it operates directly on private variables. Index is 0-based and can be the string "end", representing the last element of the list.

mylength ObjectVar

Considers the specified object variable to be a list and returns the number of elements in this list. This command is similar to the standard "llength" command, but it operates directly on private variables.

mysearch ObjectVar Pattern ?Mode?

Considers the specified object variable to be a list and searches for Pattern. Pattern matching is indicated by the parameter Mode, which can be either -exact, -glob or -regexp. The default is -exact. The command returns the 0-based index of the first element that matches Pattern, otherwise -1.

myconcat ObjectVar ?List ...?

Concatenates all given lists to the specified object variable. Unlike the standard "concat" command it directly manipulates the object variable and returns the new value.

myappend ObjectVar ?Value ...?

Considers the specified object variable to be a list and appends all given values as new elements to this list. This command is similar to the standard "lappend" command, but it operates directly on object variables.

myinsert ObjectVar Index ?Element ...?

Considers the specified object variable to be a list and inserts all given elements at the position indicated by Index. Index is 0-based and can be the string "end", representing the last element of the list. Unlike the standard "linsert" command it directly manipulates the object variable and returns the new value.

myrange ObjectVar From To

Considers the specified object variable to be a list and returns all elements from the From's element to the To's element. From and To are 0-based and can be the string "end", representing the last element of the list.

mydelete ObjectVar ?Index ...?

Considers the specified object variable to be a list and deletes all elements specified by the Indexes. Indexes are 0-based and can be the string "end". The command directly manipulates the object variable and returns the new value.

myreplace ObjectVar From To ?Element ...?

Considers the specified object variable to be a list and replaces all elements from First to Last index with the given elements, if any. From and To are 0-based and can be the string "end", representing the last element of the list. Unlike the standard command "lreplace", it directly manipulates the object variable and returns the new value.

myassign List ?ObjectVar ...?

Assigns all elements in List to the given object variables, starting from the left. That is, the first element in List is assigned to the first given object variable, and so on. If there are more elements in List than variables specified, the remaining elements will be ignored. If there are more variables specified than elements in list, the remaining variables will be left unchanged.

mylist ?ObjectVar ...?

Returns the values of all specified object variables as a list.

 NOTES

Naming Conventions

When defining classes and member function and creating objects, one should be aware of the "namespace pollution" that comes with it. Basically these considerations are similar to those that apply to widgets in wish/TK.

Remember that each defined class creates two new commands and eventually a global array for the class-common variables. Each created object creates a new procedure with the object's name and eventually a global array for the object variables. Unlike regular local variables, all created commands are global by nature (within their namespace), so one must be careful in creating and destroying objects.

To avoid naming conflict, it is recommended to make use of the "this" command when creating objects within other objects. E.g. if an object creates other objects by preceeding "[this]-" to their names, there will be no conflict on global level (provided the dash "-" is not used within object names elsewhere). This method is similar to the widget hierarchy in wish/TK.

In addition, objects must be explicitely destroyed, either at the end of the procedure that created the objects, or, if the object has been created by another object, in the creating object's destructor.

Inheritance

A class can inherit the functionality of other classes, called superclasses. In this case the new class will inherit all member functions of the superclasses. Inherited member functions can be overwritten exactly once. By doing so, the member function will execute its own code instead of the inherited member's code. Overwritten member functions can still be called using the fully qualified name, i.e. the member name preceeded by the superclass' name and a colon.

Note that no constructor, destructor or member of a superclass is executed automatically. The class that inherits a superclass has to do it explicitely by calling the superclasses' constructors, destructors and member functions. Remember that constructors and destructors are represented as regular members with the special names "+" and "-".

Because of a bug, inherited members cannot be deleted. It is instead recommended, to redefine the member and let it raise an error.

 EXAMPLES

A class that simply prints out either a default text provided in the constructor or a special text provided with the print function would look like this:

  % class PrintText { sDefault } {
    if { $sDefault == {} } {
      throw "No empty strings, please!"
    }

    my Default $sDefault
  } {} ;# no destructor code
  % member PrintText:print { {sText {}} } {
    if { $sText == {} } {
      puts [my Default]
    } else {
      puts $sText
    }
  }
  % member PrintText:getDefault {} {
    return [my Default]
  }
  % PrintText pt {}
  PrintText pt: No empty strings, please!
  % PrintText pt "My default text"
  My default text
  % pt print
  My default text
  % pt print "Special text"
  Special text
  % PrintText pt2 "Another object, another default text"
  Another object, another default text
  % pt getDefault
  My default text
  % pt2 getDefault
  Another object, another default text
  % ~PrintText pt
  % ~PrintText pt2
  

A class that uses the functionality of PrintText but extending it by printing out the time ahead of each text and by making it possible to change the default text after the object has been created, could look like this:

  % class BetterPrint:PrintText { sDefault } {
    # calling superclass constructor
    [this] PrintText:+ $sDefault
    puts "I am [this] of class [cinfo class [this]]"
  } {
    puts "[this] is gone"
  }
  % member BetterPrint:setDefault { sDefault } {
    # here is a new member of BetterPrint
    my Default $sDefault
  }
  % member BetterPrint:print { {sText {}} } {
    # this overwrites the inherited member PrintText:print
    puts -nonewline "[clock format [clock seconds] -format %H:%M]: "
    # but we still can call it:
    [this] PrintText:print $sText
  }
  % BetterPrint bp FirstDefault
  I am bp of class BetterPrint
  % bp getDefault
  FirstDefault
  % bp setDefault SecondDefault
  SecondDefault
  % bp print
  08:15: SecondDefault
  % bp print "Now with time stamp"
  08:16: Now with time stamp
  % ~BetterPrint bp
  bp is gone
  

 INSTALLATION

 General

mkClasses is written in C and comes with a DLL for Windows. On Unix, the package needs to be compiled into a shared library first (see below). mkClasses works with Tcl/Tk version 8.0 and higher. It has been stubs-enabled with version 8.2.

To install, place the directory "mkClasses1.0" in one of the directories contained in the global Tcl variable "auto_path". For a standard Tcl/Tk installation, this is commonly "c:/program files/tcl/lib" (Windows) and "/usr/local/lib" (Unix).

Compiling

To compile the package, just provide the correct path to "tcl.h" and link against "tcl8x.lib" (Windows) or "libtcl8x.so" (Unix) respectively. If you use stubs, define USE_TCL_STUBS and link against "tclstub8x.lib" (Windows) or "libtclstub8x.a" (Unix) instead.

For Visual C++, the following command should work:

  cl /I c:/progra~1/tcl/include /D USE_TCL_STUBS /c mkClasses10.c
  link c:/progra~1/tcl/lib/tclstub83.lib /dll mkClasses10.obj

On Linux 2.2, this here works fine:

  gcc -shared -DUSE_TCL_STUBS -ltclstub8.3 -o mkClasses10.so mkClasses10.c

Test

Test the installation by opening a tclsh or wish and entering "package require mkClasses". The string "1.0" should appear. If it fails, "cd" into the directory "mkClasses1.0" and load it directly with "load ./mkClasses10.dll" (Windows) or "load ./mkClasses10.so" (Unix). If no error occured, it succeeded and something must be wrong with the location of "mkClasses1.0".

 CHANGES

No changes - Initial version.

 AUTHOR

Michael Kraus
mailto:michael@kraus5.de
http://mkextensions.sourceforge.net