Up:
  1. TkScript Reference Guide » Objects
TkScript

reference guide | Objects


 
Table of Contents:

1. Objects
An Object is the base type for all native C++ classes as well as script classes.
 
An Object itself can never be instantiated, only derived classes can.
 
A reference to an instance of a derived class can be cast down to one of its base class types.
All classes can be cast down to the Object class type.
 
Script classes support multiple inheritance, i.e. a script class can have many base classes. However, the reference to a script class is always the same, no matter which base class type it is currently cast down to.
 
Example for casting down an Object reference to a base (script-) class type:

class A { int i; }
class B { float f; }
class C { String s; }

class Mixed extends A, B, C { }

Mixed m;
A a <= m;
B b <= m;
C c <= m;
trace "a="+#(a); // Variables "a", "b" and "c" all point to
trace "b="+#(b); // the same instance of script class "Mixed"
trace "c="+#(c); // but each variable only "sees" the interface of
// script class "A", "B" respectively "C".

 
Native API classes also support multiple inheritance (since C++ does) but it is not recommended to actually use this for plugin classes (e.g. because of shifting base pointers!).
 
Example for casting down an Object reference to a bass class type:

Stream s <= StdErrStream;
s.writeString("hello, world.\n", 0, 0); // write all characters to stderr
1.1. The YAC_Object interface
Up:
  1. TkScript Reference Guide » Objects » The YAC_Object interface
Each C++ class that is derived from the YAC_Object class and implements the The YAC_Object interface plugin interface can be cast to the Object datatype.
 
The Object datatype provides an interface to several core features like
1.1.1. Type conversions
  • conversions from/to String s, ints, floats or generic Object s

 
Example:

String s = 42; // convert integer to String object
int i = s; // convert String object to integer
float f = i; // convert int to float
Double d <= Double.Newf(f); // convert float to (new) Double object
s = d.string; // Convert Double to string

 
The yacToString() method is called when converting a generic Object to a String :

Double d = PI;
String s = d; // implicit call of "yacToString"
trace s;

String t;
d.yacToString(t);
trace t;
1.1.2. Operators
  • e.g. assignment, comparison, arithmetic operators
  • query operator priority

 
Example:

Double d = 1 / Double.Newi(3);
1.1.3. Array access

 
Example:

Object o <= new IntArray; // cast IntArray to basic Object type
o[10] = 42; // Write array element
print o.yacArrayGetNumElements(); // Call generic Object interface method
IntArray ia <= o; // cast back to IntArray
print ia.numElements; // Call IntArray getNumElements() method
1.1.4. Hash table access
  • Hash table access
    • read/write/unlink elements
    • uses the yacHashSet(), yacHashGet() YAC interface methods internally
    • Native API classes that support the hashtable interface are .e.g HashTable.

 
Example:

Object o <= new HashTable; // cast HashTable to basic Object type
o["myelement"] = 42; // Write hashtable element (yacHashSet())
o.yacHashSet("elem2", #(2PI)); // Set hash element using Object interface method
HashTable ht <= o; // cast back to HashTable
print ht.numElements; // Call HashTable getNumElements() method
1.1.5. Iterators
  • query iterator for an array-like class

 
Example:

HashTable ht <= #[a=1, b=2.2, c="3"];
String key;
foreach key in ht
{
trace "ht[\""+key+"\"] = " + ht[key];
}
1.1.6. Streams
  • (File-)Streaming
    • seeking
      • adjust read/write offset relative to beginning, current position or end of stream (if available)
    • read/write
      • 8,16,32bit int/float values
      • generic Objects
      • Strings
      • Buffers
    • Supports transparent byte order conversion (big vs. little endian)
    • (de-)serialization
      • Single objects
      • Complex data structures (i.e. recursively)
    • Native API classes that support the Stream interface are File, Buffer, PakFile, StdOutStream, StdInStream, StdErrStream.

 
Example:

String s; s.alloc(1024);
stdout "Enter something: ";
StdInStream.readLine(s, 1024);
stdout "\nYou entered \""+s+"\".";
StdOutStream.i8 = '\n'; // Write single byte

 
Also see The stream operator, Tags.
1.1.7. Reflection
  • C++ Reflection
    • query/invoke native class methods
    • query native constants
    • query/invoke native callbacks / signals
  • Meta class reflection interface
    • query script class names
    • query script class members
    • invoke script class methods
  • The TKS API class provides a couple of helper methods to deal with reflection
  • Reflection should be used with care and only when appropriate !

 
Example:

Object o;

// List methods in "TKS" native API class
o <= TKS;
print o.yacMethodGetNames();

// List constants in "TKS" native API class
o <= TKS;
print o.yacConstantGetNames();

class MyClass { test(String s) { print "hello, "+s; return String("new"); } }

o <= new MyClass;
Value r;
TKS.evalMethodByName(o, "test", {"world."}, r);
1.1.7.1. meta classes
Script class objects can also be cast to Object pointers.
 
The YAC_Object interface provides a so-called metaclass interface to handle script classes.
 
The metaclass interface is used to query the actual name of a script class object, read/write members values and execute methods.
 
Example:

class MyClass {
int i = 42;
float f = 2PI;
String s = "hello, world.";
}
MyClass mc;
trace "class<"+mc.yacMetaClassName()+"> #properties="+TKS.getNumProperties(MyClass);
1.2. Method naming conventions
TkScript supports a shortcut way of accessing getter / setter methods of C++ native objects.
 
Native methods whose name starts with get respectively set can be treated like object properties.
 
Example on how to use the "read property" shortcut:

String s = "123";
trace s.getLength(); // Use regular method to read "length" property
trace s.length; // Use "property" shortcut to call "getLength()" method

 
Example on how to use the "write property" shortcut:

Value v;
v.setIntValue(42); // Use regular method to write "intValue" property
v.floatValue = PI; // Use "property" shortcut to call "setFloatValue()" method
2. Object references (pointers)
Up:
  1. TkScript Reference Guide » Objects » Object references (pointers)

Object references are used to avoid time and memory costly copies of objects, for instance when object parameters are passed to functions or methods.
 
More than one Value container may point to one and the same object but only one pointer at a time can have the ownership for an object. A container object, e.g. a Variable, is only allowed to delete an object if it has the ownership for it (in form of a deleteme flag).
 
The special variable type Object represents an untyped object pointer (which technically points to a YAC_Object C++ object).
Actually it is not totally untyped since the Object class provides a couple of core interface methods, e.g. for arrays and hashtables, operators, reflection, streams etc.
 
Pointers/object references do not include type information about the object they point to; this information is rather obtained from the object itself given that it is allocated and not null.

Object o;

// Allocate new String and print out C++ class name
o <= new String;
print o.yacClassName(); // => "String"

// Script classes are "meta classes" in the sense that although they all use the
// C++ "Class" type, they can still provide totally different datatypes.
class MyClass { }
o <= new MyClass;
print o.yacClassName(); // => "Class"
print o.yacMetaClassName(); // => "MyClass"

A pointer, or reference to an object, may point to null meaning that the pointer is unset:

Object o <= new String;
o <= null; // Assign null pointer and delete String instance
2.1. Typed pointers

In order to call methods or access members, an object has to be cast to a specific type.
 
Example:

// Pointer assignment, t2 und t will point to the same memory area
// after this assignment. t will be the owner, t2 is deleted before
// the assignment
Time t, t2 <= t;
t2.now();

 
Example:

// Pointer assignment, unbound pointer returned by "new" expression
// is bound to an (untyped) variable which previous content is
// deleted before the assignment
Time t <= new Time;
t.now();
2.2. Untyped pointers

The datatype Object is used to store references to arbitrary objects.
 
Incompatible assignments will only be detected during runtime and will throw ClassTypeMismatch exceptions when an object is accessed.
 
Example:

// unbound pointer returned by "new" expression is bound to an // untyped variable
Object o <= new Time;
// this pointer is now unlinked from "o" and bound to variable "t"
Time t <= deref o; t.now();
// the variable t becomes the owner of the object after the pointer
// assignment (<=). Since 'o' is already unbound, the following
// statement does not affect 't':
o <= null;

 
Example:

String s = "hello, world.";
Object o <= s; // Assignment to untyped object variable
String t <= o; // Assignment to typed object variable
trace "t="+t; // Debug-output of string content
trace "o="+o; // Debug-output of memory address and type
2.3. ClassTypeMismatch
An assignment of a type incompatible object datatype to a typed object variable will cause the script-engine to raise a ClassTypeMismatch exception when the variable is used in a statement or expression that needs to access the object (e.g. a method call or member access).
 
Example:

String s;
Stream stream <= s;
try
{
print stream.offset; // Raises "NativeClassTypeMismatch" exception
}
catch(NativeClassTypeMismatch e)
{
trace "e.message=\"" + e.message +"\".";
}
2.4. InvalidPointer
If a script tries to dereference a null Object reference or if the reference points to an already deleted Object, an InvalidPointer exception is raised by the runtime.
 
Example:

String s <= null;
try {
s.append("d'oh");
}
catch(InvalidPointer e) {
trace "e.message=\""+e.message+"\".";
}

 
Note: It is considered bad practice to catch InvalidPointer exceptions since they are an indication for a more serious software problem !
 
Also see Debugging.
3. Spawning new objects
Up:
  1. TkScript Reference Guide » Objects » Spawning new objects
3.1. Via variables

An object variable is usually initialized with an instance of the respective Object derived class after script compilation.
 
Example:

String s; // "s" initially holds a new instance of String

 
An Object variable that initially points to null is created as following:

String s <= null; // "s" initially holds a reference to "null"

 
Untyped Object references are never instantiated:

Object o; // "o" initially holds a reference to "null"
trace #(o);
3.2. Via array variables
Up:
  1. TkScript reference guide / Expressions » The [] array expression

Arrays of int or float atomic values are implemented as objects.
 
Example:

int ia[100]; // Creates an IntArray object that can hold 100 elements
float fa[100]; // Creates a FloatArray object that can hold 100 elements
Object oa[100]; // Creates a PointerArray object that can hold 100 elements
String sa[100]; // Creates a StringArray object that can hold 100 elements

 
It is recommended to avoid this syntax and use the respective Object class names instead. Not all code paths in the script parser can handle this syntax.
For example, arrays in parameterlists do not support the array size expression:

function MyFunction(int ia[], float fa[], Object oa[], String sa[]) {
trace "ia="+#(ia)+"\nfa="+#(fa)+"\noa="+#(oa)+"\nsa="+#(sa);
}
MyFunction([1, 2, 3], [1.1, 2.2, 3.3], [null, Integer, Float], ["a", "b", "c"]);

 
Also see The [] array expression.
3.3. Via "new"
The new expression is used to allocate a new object:

String r <= new String;
3.4. Via stack frames
Some objects are created automatically when constructing the stack frame of a script method or a function with local variables:

function LocalVarTest(local String s) {
trace #(deref s);
}

 
Script class example:

class MyClass {
public method localVarTest(local String s) {
local Double d = 2PI;
trace #(deref s);
}
}
MyClass mc;
mc.localVarTest(String("hello, world."));

 
Note: Local variables must be used when a function or method (-variable) is re-entrant or recursive !
3.5. Via script classes
Object-type class members are created automatically when a new instance of a class is created:

class MyClass {
String s;
Double d = PI;
}
// declare variable "mc" and create new instances of "MyClass"
// and its members "s" and "d"
MyClass mc;

 
There is an exception, though: When a class member is an instanceof the class that it is declared in, no object instance will be created (in order to avoid infinite recursion!).
3.6. Via auto-boxing
Some objects are created automatically when e.g. passing int or float values for object-type function arguments:

function AutoboxingTest(Object o) {
trace "o="+#(deref o);
}
AutoboxingTest(42);
3.6.1. Un-boxing
Objects are automatically converted to 32 or 64bit integer/floating point numbers or Strings:

Double d = 2PI;
float f = d; // Automatically convert Double object to 32bit floating point

String s = "0x2A";
int i = s; // Convert String to 32bit integer
3.7. Via initializer expressions

Some objects are created by using Object initializer expressions:

Integer io <= Object(42);
String s <= String(PI);
Value vo <= #("my value");
IntArray ia <= [1, 2, 3];
HashTable ht <= #[a=42, b=PI, c="hello, world."];
ListNode ln <= {42, PI, "hello, world."};

 
Also see The [] array expression, The #[] hash table expression, The #() value expression, The {} list expression.
3.8. Via API methods or functions
Some API methods return new Objects that can be assigned to a value container, e.g. a variable or an array element.
 
Example:

// Grab ownership for returned object and store in "r"
String r <= "hi".replace("i", "ello, world.");

// Simply copy returned object to "s" and let it be deleted afterwards
String s = "hello".append(", world.");

// Does not spawn a new object since "append" is called in a statement
s.append("\n");
4. Deleting objects
Up:
  1. TkScript Reference Guide » Objects » Deleting objects
4.1. Automatic deletion
An object is deleted when its container object is deleted and if the container has the ownership for the given object.
 
Example:

class C {
String s; // will spawn a new String object everytime "C" is instantiated
}
C c; // new instances of class C and its String object member "s"
c <= null; // Deletes class instance and its String object member "s"

 
An object is deleted when a new object is assigned to a container object/variable, which holds a deletable reference to the respective object.
 
Example:

String s;
// delete String object stored in "s" and replace by reference to constant String
s <= "hello, world."

 
Variables are automatically deleted at exit so you usually do not have to delete any objects manually.
 
Example:

String s; // will be deleted at exit when variable "s" is being cleant up

 
Function variables and arguments declared with the local keyword will automatically be unset/deleted when a function call returns.
 
Example:

function MyFunction() {
local String s; // will be deleted when function call returns
}
4.2. Manual deletion

An object is deleted by assigning null to a container that a) stores the object and b) has the ownership flag for the object.
 
Usually, it is not necessary to manually delete objects. A developer should be careful with the object ownerships, though.
This means that you should always be aware of which value container has the ownership of any given object.
 
While this sounds harder than it actually is, I recommend to follow the RAII design pattern and treat objects/memory as resources, like you would do with e.g. file handles. The rest mostly falls into place by itself.
 
Example:

String s; // new instance of String object
s <= null; // Deletes String object and replaces it by a null pointer

 
Example:

class Something {
Buffer blob; // Some binary "blob"
}
Something x;
x <= null; // Delete the object and its child objects (i.e. "blob")
5. Memory managment
Up:
  1. TkScript Reference Guide » Objects » Memory managment

TkScript does not use garbage collection but rather utilizes a simple (but effective and fast!) object ownership system.
 
Each object reference stores a flag which hints the script engine whether it is safe to delete an object.
 
5.1. Unlinking an object
The deref expression is used to unlink deletable objects from variables and make them volatile, so they can be bound to a new container.
 
The <= pointer assign statement assigns a new container for a possibly "unbound" object value.
 
Example:

String t <= new String;
String s <= deref t; // Grab object pointer and ownership for "t"

 
Container objects that also store the object ownership, e.g. Value or PointerArray, usually support a method to unlink the ownership and object pointer and return an "unbound" pointer.
 
Example:

Value v <= #(new String);
// Grab object pointer and ownership for
// "new String" stored in container "v"
String s <= v.getDeref();
5.2. Creating object copies
the Object() and String() expressions are used to create copies of int, float or non-deletable objects so it becomes safe to store them in a (Object) container.
 
Example:

class MyClass {
Object my_object_copy;

public method copy(Object o) {
my_object_copy <= Object(o); // Create copy of argument
}

public method getString() : String {
if(null != my_object_copy)
{
String r;
r.empty();
if(my_object_copy.yacToString(r))
{
return r;
}
else
{
return "null";
}
}
}

}

MyClass mc;
mc.copy("hello, world.");
print mc.getString();

 
If an atomic 32bit int or float value is passed to the Object() expression, it will be converted to an Integer respectively Float object:

Integer io <= Object(42); // Box int value "42" into an "Integer" Object
Float fo <= Object(PI); // Box float value "PI" into a "Float" Object
io = 23; // Using native value assignment:
fo = 2PI; // ..

 
If an atomic 32bit int or float value is passed to the String() expression, it will be converted to the default ASCII representation:

String si = 42; // Convert integer to string "42"
String sf = PI; // Convert integer to string "3.14159"..

 
In order to preserve the original value, type and ownership of an expression's return value, use the #() expression to wrap it in a Value Object.
5.3. Wrapping values in container objects
The #() value expression is used to wrap unbound objects in a generic value container.
 
The {} list expression is used to wrap a list of unbound objects in a list of generic value containers.
 
Variables and class members can also store int / float values and Object references along with the ownership flag.
 
Since variables are usually strongly typed, values assigned to them are converted to the variable's type.
 
This does not hold for objects, though. Type checking for objects is done on-access. Mixing up object types may result in ClassTypeMismatch or InvalidPointer exceptions, for example.
5.4. Objects in argument lists
Objects (and Strings) are always passed by reference (call-by-reference) while the scalars int und float are passed as a copy (call-by-value).
 
Arguments to C/C++ calls are always passed by reference, i.e. the memory management information (ownership flag) is lost.
 
If you need to pass deletable objects, variable argument types and/or argument lists to native functions or methods, please use e.g. the Value or List objects which can be created on-the-fly by using the #() and {} expressions.
 
Example:

PointerArray pa;

// Add non-deletable reference to String to pointer array
pa.add("hello, world.");

// Add deletable copy of String to pointer array
pa.add(#(Object("hello, world.")));

 
Example:

function MyFunction(String _s) {
// directly modifies the parameter value
_s.append(", world.");
return _s;
}
String hello = "Hello";
trace MyFunction(hello);

 
Example on how to pass argument lists to methods called via the Reflection interface:

// Declare user defined datatype
class MyClass {
public method myMethod(int i, float f, String s) {
trace "i="+#(i)+" f="+#(f)+" s="+#(s);
}
}
MyClass mc; // declare instance of "MyClass"
Value r; // "r" receives the return value of the called function / method
// Wrap argument list in "ListNode" objects using the {} list expression
TKS.evalMethodByName(mc, "myMethod", { 42, PI, "hello, world." }, r);

// Regular invocation of class method
mc.myMethod(42, PI, "hello, world.");

 
Example on how to pass argument lists to functions called via the Reflection interface:

function MyFunction(int i, float f, String s) {
trace "i=" + #(i) + " f=" + #(f) + " s=" + #(s);
return String("new");
}
Function f <= MyFunction;
var v <= f.eval({42, PI, "hello, world."});
trace #(deref v);

 
see TKS for more details about the reflection helper API.
5.5. Preallocated container objects
In order to consolidate/optimize memory allocation, some array classes have variants that come with a pre-allocated element array.
 
If the pre-allocated size is exceeded, a new element array will be allocated.
 
Please see IntArray8, IntArray16, IntArray32, IntArray64, IntArray128, FloatArray8, FloatArray16, FloatArray32, FloatArray64, FloatArray128, String8, String16, String32, String64, String128 for details.
 
Example:

class CPerson {
String32 screen_name; // String object has space for 32 characters
String64 name; // String object has space for 64 characters
String64 surname; // String object has space for 64 characters
String128 email; // String object has space for 128 characters
// ..before buffer re-allocation

public static New(String _screenName, _name, _surname, _email) : CPerson
{
local CPerson c;
// Copy parameter character sequences to member fields
c.screen_name = _screenName;
c.name = _name;
c.surname = _surname;
c.email = _email;
return deref c;
}
}

// Create new "CPerson" object and
// copy characters to "local" class member fields
CPerson c <= CPerson.New("me", "my name", "my surname", "my email");
trace "c.screen_name = \""+c.screen_name+"\".";
trace "c.name = \""+c.name+"\".";
trace "c.surname = \""+c.surname+"\".";
trace "c.email = \""+c.email+"\".";
5.6. Caveats
TkScript allows you to mess up pointers and potentially cause crashes by using references to already deleted objects.
 
Each object has a validation_tag field which will read YAC_VALID_TAG as long as the object is alive. The field is changed to YAC_INVALID_TAG when the object is deleted.
 
The runtime will raise an InvalidPointer exception if an illegal pointer is detected but there are chances that this will cause a segmentation fault.
 
There are of course chances that the memory region has already been replaced by a new object. However, in practice this mechanism works quite well and should help finding dead object references.
 
You can use the -ndo command line option to help with debugging (at the expense of an increased memory consumption). This is in so-far useful as that objects will only be marked dead and their memory will never be replaced by a new object.
 
Also see Debugging.
 
5.6.1. InvalidPointer
This example deliberately causes an InvalidPointer exception.

String s <= new String;
String t <= s; // assign reference to "s"
s <= null; // delete string since "s" has the ownership for it
t.append("oh noes!"); // raise exception since the String object is already free'd.

 
Also see InvalidPointer.
6. Container objects
A number of container classes is provided by the core TkScript API.
 
Classes that can store object references along with the ownership flag are Class, ClassArray, HashTable, List, ListNode, ObjectArray, PointerArray, Script, StringArray, TreeNode, Value, ValueArray, Variable.
 
Example:

PointerArray pa;
String s <= String("hello, world.");
pa[0] = deref s; // unlink ownership from variable and transfer to array element 0
trace #(pa);

 
When passing deletable object references to C++ methods, you often need to wrap them in a Value object. Example:

PointerArray pa;
// duplicate constant String, wrap new String Object in a Value and
// pass it to the PointerArray.
// The PointerArray will take over the ownership for the String Object
// by unlinking it from the Value object.
pa.add(#(String("a copy of Hello, World.")));
trace #(pa);

 
If the object reference is stored in a variable, you would do something like this:

PointerArray pa;
String s <= String("a copy of Hello, World.");
// Unlink ownership for String object from variable and wrap in a Value object
// the PointerArray grabs the ownership for an Object reference if the Value has it.
pa.add(#(deref s));
trace #(pa);


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