Up:
  1. TkScript Reference Guide » Classes
TkScript

reference guide | Classes


 
Table of Contents:

 
1. Introduction

A class is a structure which is composed of the scalars int, float, and Object, i.e. instances of C++ resp. user defined script classes.
 
Classes are used to extend applications by new datatypes.
 
The elements of a class are called members, fields or properties. Also see Members.
 
Similar to basic datatypes like e.g. int or float, classes first need to be instantiated before their members and methods can actually be accessed.
 
Class instances are called objects (see Object, TkScript reference guide / Objects).
 
A method is basically a function that can operate on the instance of a script class. Methods can see the script class members / fields and read/modify them. Also see Methods.
 
Class functions (static methods) and constants can be used without a class instance (Object). The class is just used as a scope in this case.
 
Further script class features include
 
Please notice that this section will mainly focus on script classes. For more information about objects in general (and C++ classes), see TkScript reference guide / Objects.
2. Declaration
Up:
  1. TkScript Reference Guide » Classes » Declaration

The class statement is used to declare a script class (see The class statement).
 
A class declaration is placed in the currently active namespace. The class name must be unique in the active namespace.
 
Example:

// Declare class in default namespace
class C {
exec() { print "C::exec()"; }
}

namespace test; // create/select namespace "test"

// Declare class in namespace "test"
class C {
exec() { print "test::C::exec()"; }
}

namespace default; // select default namespace again

C c1;
c1.exec(); // => call "exec" method of class "C" in default namespace

test::C c2;
c2.exec(); // => call "exec" method of class "C" in "test" namespace
3. Polymorphy
Up:
  1. TkScript Reference Guide » Classes » Polymorphy

A class may have up to 32 base classes from which it inherits the respective base class' methods, members, constants and exceptions.
 
A reference to a class can be cast down to any of its base class types.
 
The Object class is an implicit base class of every script class.
 
Example:

class A {
String a_member;
}
class B {
float b_member;
}
class C extends A, B { // "C" has base classes "A" and "B"
int c_member;

public method printMembers() {
trace "a_member = " + a_member;
trace "b_member = " + b_member;
trace "c_member = " + c_member;
}
}

C c;
c.a_member = "hello, world.";
c.b_member = PI;
c.c_member = 42;
c.printMembers();
3.1. Base class order
The base class order in a class declaration affects the class' vtable (see Method vtables) setup:

class A {
test() { print "A::test()"; }
}
class B {
test() { print "B::test()"; }
}
class C extends A, B {
}
class D extends B, A {
}

C c;
c.test(); // => call "A::test()"

D d;
d.test(); // => call "B::test()"
3.2. Run time type checks

The class type of an Object reference can be tested at runtime by either using the instanceof expression or the yacInstanceOf() and yacMetaClassInstanceOf() Object class methods.
 
Example:

Boolean b;

b = File instanceof Stream;
print b; // => "true"

File f;
b = f.yacInstanceOf(Stream);
print b; // => "true"

 
Also see The instanceof expression.
 
3.2.1. Automatic type checks
Run time type checks are also performed when invoking class methods or when reading/writing class members.
 
If a check fails, a ClassTypeMismatch or InvalidPointer exception is thrown by the runtime:

class A { int i=23; test() { } }
class B { int i=42; test() { } }

A a <= new B(); // invalid assignment but still possible

// Call method
try {
a.test(); // raises exception since "a" actually has type "B"
}
catch(ClassTypeMismatch e) {
trace "caught ClassTypeMismatch exception (OK).";
}

// Access member
try {
trace a.i; // raises exception since "a" actually has type "B"
}
catch(ClassTypeMismatch e) {
trace "caught ClassTypeMismatch exception (OK).";
}
4. Members
Up:
  1. TkScript Reference Guide » Classes » Members
  2. TkScript reference guide / Classes » Introduction

A member is a named value defined in the scope of a class - instance.
 
Each time a class is instantiated, its non-static members are instantiated, too.
 
Members are visible in class methods; they can also be accessed from outside script code using the objname.member syntax.
 
Member declarations may also contain initializers (e.g. int i=42; or int ia2[16]; )
 
Example:

class MyClass {
int i;
float f;
String s;
int ia[]; // same as IntArray ia; . initial size=0.
int ia2[16]; // initial size=16
FloatArray fa; // same as float fa[]; . initial size=0.
float fa2[32]; // initial size=32
HashTable ht; // initial size=57
}

MyClass obj; // instantiate class "MyClass"
4.1. Static members

A static member is basically a variable (or named value) defined in the scope of a class.
 
The main difference to a regular member is that it is not instantiated per instance but rather per class, i.e. all instances of a class "see" the same data.
 
Static member have to be prefixed with the static keyword.
 
Example:

class Configuration {
static int debug_level;
}

Configuration.debug_level = 10;
4.2. Initializers

Class members can be initialized during the instantiation of a class.
 
The = assignment operator is used to copy the result value of an initializer expression.
 
Initializers are called in the order in which they have been declared.
 
Initializers may refer to the result of previous initializers
 
Initializer expressions do not need to be constant, you may also call functions, for example.
 
The <= assignment operator is used to "grab" a reference to the (Object) result of an initializer expression.
 
Example:

class C {
int i = 42; // initialize with integer "42"
float f = PI; // initialize with float "3.1415"
String s = "i="+i+" f="+f; // s => "i=42 f=3.1415"
IntArray ia <= [1, 2, 3];
}
C c;
4.3. Object members

Object class member instances are usually created automatically when a new instance of the respective class is created or, if the class member is static, during startup.
 
However, there is one exception to this rule: When the class type of an object member is equal or derived from the current class, a null reference will be created, instead. This is done to avoid infinite recursion.
 
Example:

class MyListNode {
MyListNode next; // not instantiated to avoid recursion
Value data;
}
MyListNode ln;
trace #(ln.next); // => null
4.4. Permissions

The scope of a class member can be limited
  • not at all ("public")
  • to the current module ("module")
  • to the current class ("private")
  • to the current class and all derived classes ("protected")

 
This information hiding mechanism is especially useful when creating libraries: Certain members (and methods..) can be hidden from the application that uses the class and thus force the application to use the designated (method) interface of the respective class.
 
If no specific permission attribute is used, a script class member (or method) uses the "public" scope.
 
Example:

class MyListNode {
protected MyListNode next; // not visible outside "MyListNode"
protected Value data;

public method initValue(local var v) {
data = deref v;
}

public method appendValue(local var v) : MyListNode {
explain "Append a new listnode holding the given value and return the new node.";

local MyListNode ln;
ln.initValue(deref v); // Copy value to "data" member
ln.next <= deref next; // Old "next" becomes "next" of new node
next <= deref ln; // Insert new listnode directly after this node
return ln; // Return reference to new node
}

public method getString() : String {
MyListNode ln <= this;
local String r = "{";
int i = 0;
while(ln != null)
{
if(i++ > 0)
{
r.append(", ");
}
r.append(ln.data.string);
ln <= ln.next;
}
return deref r; // Return string representation of this list(-node)
}
}

MyListNode ln, t <= null;
ln.initValue(42); // Init "head" node
t <= ln.appendValue(PI); // Append new node
t.appendValue("hello, world."); // Append another node to new node
trace ln.getString(); // Print string representation of list to stdout

 
The previous example can also be implemented using the native List and ListNode classes:

List l;
l.addLast(#(42)); // Init "head" node
l.addLast(#(PI)); // Append new node
l.addLast(#("hello, world.")); // Append another node to new node
trace l.string; // Print string representation of list to stdout

 
Respectively using the listnode expression (see The {} list expression) :

trace {42, PI, "hello, world."};
4.5. Tags

A script class member must be tagged using the tag keyword in order to allow it to be serialized when a class is written to a Stream.
 
Example:

class Person {
tag String forename; // can be serialized
tag String surname; // can be serialized
String hash; // will never be serialized
}
Person p;
p.forename = "Peter";
p.surname = "Parker";
Stream o <= StdOutStream;
o << p; // Serialize class instance into stream

 
Also see The stream operator.
4.6. How to explicitely address a class member

Class members can be addressed by prepending the member name with the this keyword.
 
This is mostly useful when a variable or function parameter shadows the script class member in a class method.
 
Example:

class C {
String s;

init(String s) {
this.s = s;
}
}

C c;
c.init("hello, world.");

 
However, I recommend to simply prefix the argument names with the _ character to avoid this situation:

class C {
String s;

init(String _s) {
s = _s;
}
}
C c;
c.init("hello, world.");

 
A static class member can be addressed by prepending the member name with its parent class name:

class C {
static String s;
}
C.s = "hello, world.";
trace C.s;
5. Methods
Up:
  1. TkScript Reference Guide » Classes » Methods
  2. TkScript reference guide / Classes » Introduction

A method is basically a function defined in the scope of a class.
 
A method that is prefixed by the static keyword is accessible without a class instance.
 
Methods can also be decorated with the public, module, protected and private attributes to limit the scope of the respective method.
 
Methods can be synchronized per-instance, per-method, per global mutex or using manually written synchronization code. See Synchronization.
 
For more details about the function / method syntax, see TkScript reference guide / Functions.
 
5.1. Method vtables
Up:
  1. TkScript reference guide / Classes » Polymorphy » Base class order

TkScript creates a so-called vtable for each non-static class method.
 
The virtual table (vtable) is used to call methods dependent on the actual object class type. This mechanism is also called late-binding.
 
Example:

class MyLog {
puts(String s) { } // no-op interface declaration
}

class MyStringLog : MyLog {
protected String log;

puts(String s) {
log.append(s);
}
}

class MyStdOutLog : MyLog {
puts(String s) {
print s;
}
}

MyStringLog stringLog;
MyStdOutLog stdoutLog;

MyLog log <= stringLog;
log.puts("hello, world."); // => call MyStringLog::puts method

log <= stdoutLog;
log.puts("hello, world."); // => call MyStdOutLog::puts method
5.1.1. How to explicitely address methods of base classes

Base class methods can be addressed by prepending the method name with the respective base-class method name.
 
This is especially useful to call a super class method:

class MyBaseClass {
public method init() {
print "MyBaseClass::init()";
}
}

class MyDerivedClass : MyBaseClass {
public method init() {
MyBaseClass::init(); // Call super class method
print "MyDerivedClass::init()";
}
}

MyDerivedClass c;
c.init();
5.2. Forward declarations

Forward declarations of class methods can be used to separate the declaration from the actual implementation.
 
The permission attributes, return type declaration, argument list or constraints are not repeated in the implementation.
 
Example:

class MyVector {
public method init(float x,y,z); // Forward declaration of method "init"
}

MyVector::init { // Implementation of method "init"
trace "("+x+"; "+y+"; "+z+")";
}

MyVector v;
v.init(1,2,3);
5.3. Constructors

TkScript supports constructors and destructors BUT in the current runtime it cannot be guaranteed that these will actually be called.
 
The reason for this is that the c'tors and d'tors need the correct script context to run. This context is currently not available everywhere a new object can be created.
 
Example:

class MyClass {
MyClass() {
trace "construct MyClass";
}

~MyClass() {
trace "destruct MyClass";
}
}
MyClass c;

 
Please do not rely on constructors or deconstructors and use simple init / free methods instead:

class MyClass {
init() {
trace "construct MyClass";
}

free() {
trace "free MyClass";
}

static New() : MyClass {
local MyClass c;
c.init();
return deref c;
}
}
MyClass c <= MyClass.New();
c.free();
6. Constants
Up:
  1. TkScript Reference Guide » Classes » Constants

Constants can be declared in the scope of a script class.
 
In contrary to module constants, class constants may use complex initializer expressions.
 
Example:

class C {
define int INT_CONST = 2 * 21;
define float FLOAT_CONST = PI;
define String STRING_CONST = "hello, world.";
}

trace " INT_CONST=" + C.INT_CONST;
trace " FLOAT_CONST=" + C.FLOAT_CONST;
trace "STRING_CONST=" + C.STRING_CONST;

 
Also see User defined constants.
7. Exceptions
Up:
  1. TkScript Reference Guide » Classes » Exceptions

Exceptions can be declared in the scope of a script class.
 
A class exception is inherited by all derived classes.
 
Class exceptions can be derived from global exceptions.
 
A specific base class exception type can be selected by prepending the exception name with the respective class and/or namespace name (separated by ::).
 
A global exception type can be addressed by prepending the exception name with the :: keyword.
 
For more about exceptions, see TkScript reference guide / Exceptions, The exception statement.
 
Example:

define exception MyGlobalException; // declare global exception

class MyBaseClass {
define exception MyBaseException; // declare class exception
}

class MyClass extends MyBaseClass {
define exception MyException;
define exception MyDerivedException extends MyBaseException;
define exception MyDerivedException2 : MyBaseClass::MyBaseException;
define exception MyDerivedException3 : ::MyGlobalException;

static ThrowIt() {
throw MyDerivedException2 "something went terribly wrong";
}
}
try {
MyClass.ThrowIt();
}
catch(MyBaseClass::MyBaseException e) {
trace "caught " + e.fullName + " e.message=\"" + e.message + "\".";
}
8. Delegates
Up:
  1. TkScript Reference Guide » Classes » Delegates

A delegate is basically a method that can run in the scope/context of a class instance although it has not actually been declared within the respective class.
 
This means that the delegate method can access all members and methods of a class, just like it were a regular class method:

class C {
define String CMD = "my_cmd_callback";
int i = 42;

public method exec() {
// Delegate call to the method that is currently bound to "my_cmd_callback"
delegate (CMD) ();
}
}

delegate C:print_i() {
// Delegate method is allowed to access class member "i"
print "i="+i;
}

C c; // Instantiate class

// Assign delegate method "print_i" to delegate slot "my_cmd_callback"
delegate c : C.CMD = print_i;

c.exec();

 
This mechanism is mainly useful to implement callbacks and object-specific behaviours without deriving a new class type.
 
Example:

class AbstractAction {
getActionName() { return "click"; }
}

class Button {
consumeAction(AbstractAction _ac) returns boolean {
// Delegate method call
return delegate "xfm__"+_ac.getActionName()(_ac);
}

callPrintTextHandler() {
// Delegate method call to callback "print"
delegate "print"();
}
}

Button xfm1;

// ---- Declare method that can be plugged into script object
delegate Button:Print_HelloWorld() {
print "hello, world.";
}
delegate Button:xfm1__click(AbstractAction _ac) {
print "delegate \"xfm1__click\" called.";
return true; // consumed..
}

delegate xfm1 : "xfm__click" = xfm1__click; // add method delegate
delegate xfm1 : "print" = Print_HelloWorld; // add method delegate

xfm1.callPrintTextHandler();
xfm1.consumeAction(new AbstractAction);

 
Also see The delegate statement.


auto-generated by "DOG", the TkScript document generator. Wed, 31/Dec/2008 15:53:35