At the heart of the Keystone application framework is the Keystone C language. The Keystone Application Framework itself, and programs that you write using Keystone are writen in Keystone C. Currently, the language is implemented as a preprocessor which operates between the source code that you write, and the C compiler which compiles this code. The preprocessor thus translates from the Keystone C language to straight C suitable for the C compiler. This flow of operation is illustrated as follows:
The Keystone language is itself a superset of the C language, and is based heavily on C++. The design of the language has been made with these objectives
The Keystone C language is heavily intergrated with the Keystone C framework, and should not be considered in isolation. This document should read in reference to other Keystone documentation, and links are made in this document to this where relivant.
This document is a reference to the Keystone C language, it is however not intended an introduction to programming, Object Oriented Design or the C programming language. Excellent material exists on these topics, and is recommended in the references document.
This document assumes a familiarity with the C & C++ programming languages of the reader
Modules are defined by the 'MODULE::' definition in the source. The syntax is
MODULE::<IMPORT | INTERFACE | IMPLEMENTATION | END>
A template for a module looks like this:
/*==========================================================================*/ MODULE::IMPORT/*============================================================*/ /*==========================================================================*/ #include "framework.h" /*==========================================================================*/ MODULE::INTERFACE/*=========================================================*/ /*==========================================================================*/ /* Modules Interface section code goes here */ /*==========================================================================*/ MODULE::IMPLEMENTATION/*====================================================*/ /*==========================================================================*/ /* Modules Implementation section code goes here */ /*==========================================================================*/ MODULE::END/*===============================================================*/ /*==========================================================================*/
bool active = TRUE;type 'bool' type operated in the same way as the C language boolean evaluation, meaning when a boolean is evaluated as an integer, a value of 0 indicates 'FALSE' and a non-0 value indicates 'TRUE'. For example
int level_A = 20, level_B = 20;
bool active = TRUE;
if (active) {
active = (level_A == level_B);
}
Keystone C uses a syntax simular to C++ to define classes. A class is defined with this syntax:
class {class name} : {base class} {
public:
/*public class members and methods */
private:
/*private class members and methods */
};
'base class' in manditory, there is no such thing as a baseless class in Keystone.
Every class has two static text strings associated with it, a name and (optionally) an alias. This name information may be retrieved at run time by the application. Class name and aliases are used, for example, in loading and storing objects to persistent storage in XML
The classes 'name' is generated automatically when the class is defined and may not be modified. The classes name is a text string with the content the same as the name used to define the class. For example, a class defined as 'CExample' will have a class name of "CExample".
The class 'alias' is a more informal reference to the class. Class aliases are specified by a
ALIAS<>definition inside the class definition. A class does not need to have an alias, but it may have only one alias. For example:
class CExample : CObject {
ALIAS<"demo">;
};
This defines a class with name 'CExample' and alias 'demo'
Methods operate on an object (an instance of a class) and modify that objects internal state. There are two parts to a method definition:
It is illegal to define a method body without a method prototype. The method prototype is declared inside the class definition, with this syntax:
{return type} {method name}({arg1, arg2, ...});
The method body is declared with this syntax
{return type} {class name}::{method name}(arg1, arg2, ...}) {
{method body}
}
Inside the method body, a pointer to the object being operated on by the
method may be refered to using the keyword 'this'
void CExample::value_set(int value) {
this->value = value;
}
A method is involked from an application with this syntax:
{result} = {class name)({object}).{method name}({arg1, arg1, ...});
The following is an example defining and calling methods
class CExample : CObject {
public:
bool method_foo(void);
bool method_bar(void);
}
bool CExample::method_foo(void) {
return TRUE;
}
bool CExample::method_bar(void) {
return CExample(this).method_foo();
}
Objects (instances of a classes) are created and destroyed using the 'new' and 'delete' operations. Objects may be created either from statically allocated memory, or dynamically, from the heap.
To create an object from statically allocated memory:
new({&object}).{class name}({arg1, arg2, ...});
To create an object dynamically
{object pointer} = new.{class name}({arg1, arg2, ...});
A simple example using both statically & dynamically allocated objects:
CExample demo_static, *demo_dynamic; new(&demo_static).CExample(); demo_dynamic = new.CExample();The following illustrates the life of an object:
On construction, the classes 'new' method is called (if present), then the classes constructor is called with the arguments passed in the 'new' operation. The classes constructor is the classes method which has the same name as the class, so for the class 'CExample', method
CExample::CExample()is the constructor
Objects are deleted using the 'delete' operation:
delete({&object});
On deletion:
CExample::~CExample()is the destructor
The following is a typical example of a class definition in relation to construction/destruction
class CExample : CObject {
private:
CString string;
int value;
public:
void new(void);
void CExample(int value);
void ~CExample(void);
};
void CExample::new(void) {
new(&this->string).CString(NULL);
};
void CExample::CExample(int value) {
this->value = value;
CString(&this->string).printf("%d", value);
};
void CExample::~CExample(void) {
delete(&this->string);
};
OBJECT<{class name}, {object name}>;
A pointer to an object of any class, or a pointer to void data may be cast to a pointer to another class. This is done using casting syntax, which is
{class name}({object});
It is valid to cast:
If the cast in invalid, and exception will be thrown at run time when the cast is attempted. The exception throws is
EXCEPTION<CObject,InvalidCast>
See the section Exceptions in this document for more information on exceptions.
Every class defined by the application framework, and classes defined by applications you write has a small associated information structure which you may use to retrieve information about the class at run time. The structure has the name 'CObjClass' and may be retrieved in one of two ways:
CObject::obj_class()method on that object
&class({class name})
For example to check if the object 'object' is of class 'CExample':
if (CObject(object).obj_class() == &class(CExample)) {
/* test has proven true */
}
Class attributes allow members of a class to be made avaliable to application users. A class member which is marked as an attribute may be:
Attributes are marked when a class is defined, with this syntax:
ATTRIBUTE<{type} {name}, "{alias}"}>
'{alias}' is optional, if ommitted the attribute will use "{name}" as it's name
The following example defines a class with a number of attributes:
class CExample : CObject {
public:
ALIAS<"example">;
ATTRIBUTE<int width>;
ATTRIBUTE<int height>;
void new(void);
void CExample(void);
void ~CExample(void);
}
When this example class is stored persistently as XML, the result will look
like:
<?xml version="1.0"?> <example width="100" height="100"/>
The use of attribute is covered with examples in the tutorial
The following attribute types are built into the Keystone framework:
In addition, the following types are defined by Keystone graphics (part of the framework) and may be used in your application:
Your application may define new attribute types for it's own use. An attribute type consistes of these parts:
typedef struct {
int hour;
int minute;
int second;
} TTime;
ATTRIBUTE:typedef<TTime>;
bool ATTRIBUTE:convert<int>(struct tag_CObjPersistent *object,
const TAttributeType *dest_type, const TAttributeType *src_type,
int dest_index, int src_index,
void *dest, const void *src) {
TTime *time;
CString *string;
if (dest_type == &ATTRIBUTE:type<TTime> && src_type == &ATTRIBUTE:type<CString>) {
time = (TTime *)dest;
string = (CString *)src;
*value = sscanf(CString(string).string(), "%d:%d:%d",
&time->hour, &time->minute, &time->second);
return TRUE;
}
if (dest_type == &ATTRIBUTE:type<CString> && src_type == &ATTRIBUTE:type<TTime>) {
time = (TTime *)src;
string = (CString *)dest;
CString(string).printf("%d:%d:%d", *time);
return TRUE;
}
return FALSE;
}