CIAO - C Inspired Active Objects

CIAO is an OOP package modelled on C++ for VFX Forth. CIAO is designed to provide easy interfacing to host operating system structures that are built around a C++ model.

The source code for CIAO is in the Lib\oop\Ciao directory, as are several example class files. To rebuild CIAO, compile the file ciao.bld.

Token and Parsing Helpers

Various utilities and factors useful for parsing text.

buffer: token-buffer
A Memory buffer used to hold the result of the last token parse. The size of this buffer comes from the environement variable MAX-CHAR and is MAX-CHAR + 1 characters in length since the string is stored as a counted string.

: new-word      \ char -- $
This is a replacement for WORD which places the output in the token buffer.

: peek-token    \ -- c-addr u
Copy the next token into the TOKEN-BUFFER without permanent change to the input specification (uses SAVE-INPUT and RESTORE-INPUT). Returns TOKEN-BUFFER as a c-addr u pair.

: drop-token    \ --
Throw away the next token *without* corrupting TOKEN-BUFFER.

: ciao-token    \ -- c-addr u
Grab the next space delimited token and return c-addr u. Fills TOKEN-BUFFER.

: bracketed?    \ c-addr u -- flag
Is the string C-ADDR U bracketed?

The THIS Stack

The heart of this OOP implementation is the concept of "THIS". Just like C++ "THIS" returns the currently active object instance pointer. Instance data is accessed via this pointer as are the "virtual" methods.

THIS is kept in a form of stack.

: >this         \ val --
Set THIS to VAL. (Preservation is taken care of in the compiler.)

: this          \ -- instance-pointer
Return the current instance pointer. Only valid within a method declaration.

CIAO Constants and Internal Data Stores

SCOPE_PUBLIC Value CurrentScope \ -- n
When defining a derived class this holds the scoping type employed.

0 Value CurrentClass    \ -- n
When defining a class this points to its CLASS structure.

0 Value DefFlags        \ -- n
The declaration flags to be employed by the next method or data member defined in the current class. Records information from control definitions such as VIRTUAL and STATIC.

0 Value CurrentDefClass \ -- class
During compilation of a code method, this value holds a pointer to the associated CLASS structure.

0 Value CurrentDefXT    \ -- xt
During compilation of a code method, this value holds a pointer to the XT of the definition. See the CIAO-COLON hook for details.

0 Value CurrentDefList  \ -- list
During compilation of a code method, this value holds a pointer to the internal method list to be used. The list will either be the classes public, protected or private chain depending on the scope at the time of the method declaration prototype.

variable class-base-mem \ -- addr
This variable holds the value of HERE after the building of CIAO. It is used to sanity check the values passed to the instance destruction definition DELETE. Any passed value between this variables value and the current HERE is in dictionary space and must be a static instance which cannot be DELETEd.

Search Order Utilities

: NSEARCH-WORDLIST      \ WIDN .. WID1 N C-ADDR U -- XT FLAG | 0
A most useful definition. FIND takes a counted string but searches the whole search-order. SEARCH-WORDLIST takes a C-ADDR U pair but only searches one wordlist. This definition combines the two, and looks through a number of wordlists for a name described by a C-ADDR U pair. Usually used in association with GET-ORDER to provide a more useful version of FIND.

: (FindClass)   \ c-addr u -- ptr | THROW
Run through the current search-order lookin for the name supplied. If the name is found then a >BODY @ is employed on the XT to look for the MAGIC_CLASS identifier. Never called directly, this definition is run from FINDCLASS via CATCH to protect against the times when the token is found but is not a class. Due to the exception handling abilities of CATCH under VFX, this operation should be safe no matter what XT it is employed against.

: FindClass     \ c-addr u -- ptr | ABORTs
Invoke (FINDCLASS) via CATCH. Will look for the token supplied and if found will ensure it really is a class definition. ABORTs with text if anything goes wrong.

Method Lists

The Method Lists hold all the required compiler information for each method within a class. In CIAO, methods don't ever actually exist as regular Forth words. Instead the act of defining a class builds the method lists. Each class has three of these, one for each valid scope (public/protected and private).

The Format of a Method List.


   | Link | Type | Param1 | Param2 | Name Len | Name Text |
   | CELL | CELL |  CELL  |  CELL  |   CHAR   |  n chars  |

Link

Pointer to start of previous list entry (or 0 for top)

Type

The type of the method (see types below)

Param1

Parameter 1, varies depending on TYPE.

Param2

Parameter 2, varies depending on TYPE.

NameLen

Length of method name.

NameText

The text for the method name.

TYPE_DATA

Describes an instance data buffer, PARAM1 is the base offset from THIS.

TYPE_STATICDATA

Describes a static data buffer. A static data buffer is placed within the global dictionary rather than being offset from THIS. The net result is that all instances of the owning class and any derived classes share the same location for this data element. PARAM1 is the address in global space of the buffer start.

TYPE_CODE

The default code method type. PARAM1 points to a CELL in global dataspace which will contain the XT of the method body as soon as it becomes available. A CODE method cannot be re-defined or rewritten and it's behaviour is inherited by any derived class.

TYPE_STATICCODE

The second type of code method. It behaves in a similar fashion to TYPE_CODE except the instance pointer THIS is not valid within the method body. These means that a static member has no access to any other member of the class which is non-static. A static member can also be invoked from a colon definition or the interpreter by using the "named scope override" syntax, which does not require an instance pointer. Primarily used to store "normal" functions in a restricted namespace. Ie "do <something> in the name of <some class>"

TYPE_VIRTUALCODE

One of the most useful syntactic additions to C++ was the virtual method. A virtual method can best be described as a method in a base class which you expect to have to modify or replace in a derived class. PARAM1 holds a 0 based index into a table of XTs called a "vtable". Each class has a vtable which in the case of a derived class is initially inherited from the superclass. A derived class can either omit its function body (and thus inherit the behaviour of the superclass) or it can define its own body which can also optionally elect to invoke the superclass's body by using the named scope override syntax. Therefore a virtual method can be either modified or replaced within the context of a derived class. A particularly useful feature of the usefulness of virtual methods can be seen later.

TYPE_CLASS

This type of method specifies a static instance of another class as being a part of the current. PARAM1 specifies the class type whilst PARAM2 specifies the offset from THIS for the instance pointer of the contained class.

TYPE_CLASSPTR

A special form of data store which holds a pointer to a class instance. PARAM1 specifies the class type and PARAM2 the offset from THIS to a single cell. This cell will hold an instance pointer which can be dynamically assigned.

The definitions which deal with lists are:

: list_link     \ *entry -- *link
Modify a pointer to a list head to point to the link field.

: list_type     \ *entry -- *type
Modify a pointer to a list head to point to the type field.

: list_param1   \ *entry -- *param1
Modify a pointer to a list head to point to the param1 field.

: list_param2   \ *entry -- *param2
Modify a pointer to a list head to point to the param2 field.

: list_namelen  \ *entry -- *namelen
Modify a pointer to a list head to point to the namelen field.

: list_name     \ *entry -- *name
Modify a pointer to a list head to point to the name field.

: .list-type    \ n --
Given contents of a list entry's type field print it's name as an ascii text string.

: .list-entry   \ *entry --
Supplied with a pointer to a list entry this definition will print its contents in human readable form.

: .list         \ *head --
Supplied with the address of a variable which points to a list entry this definition will walk backwards through the linked list performing .LIST-ENTRY on each in turn.

: +LIST         \ Type Param1 Param2 c-addr u *list-head --
Using the first 5 parameters lay a list-entry structure in the dictionary and add it to the end of the list whose anchor address is at the address pointed to by *LIST-HEAD.

Operator List

Each class has a linked list called the Operator Chain. This list contains the mapping of operator-id number against class method list entry (from above).

An operator structure entry consists of three fields, the link, the operator id number and a method-list pointer.

: oplist_link             ;
Given a pointer to an operator structure return pointer to link

: oplist_op#    1 cells + ;
Given a pointer to an operator structure return pointer to op#

: oplist_list   2 cells + ;
Given a pointer to an operator structure return pointer to *list

: .op#                  \ n --
Where possible print human readable description for operator id N

: .oplist-entry \ *entry
Display an operator structure in human readable form.

: .oplist       \ *head --
Given a pointer to the head of an operator chain from a class, call .OPLIST-ENTRY for each member.

: +OPLIST       \ op# *list-entry *head --
Add record to the operator chain anchored at *HEAD

The CLASS structure

All classes defined have the same structure:


Size       Navigation Word         Useage
------------------------------------------------------------------
CELL       class_magic             A magic 32 bit number used
                                   to signify a CLASS structure.
CELL       class_super             Pointer to parent class for
                                   a derived class object.
CELL       class_private           Method list anchor for PRIVATE
                                   definitions.
CELL       class_protected         Method list anchor for PROTECTED
                                   definitions.
CELL       class_public            Method list anchor for PUBLIC
                                   definitions.
CELL       class_opchain           Anchor for the operator chain
                                   for this class.
CELL       class_sizeidata         Size of Instance data required.
CELL       class_#vtable           Number of entries in the
                                   virtual method table.
CELL       class_pvtable           Pointer to the virtual method
                                   table.

: .class        \ "name" --
Display as much information about the class "name" as possible in a human readable form.

Method Searching

Definitions used to find a given method within a class.

: FindMethodInClass     \ c-addr u *class -- ptr SCOPE | -1
Given a string containing the method name and a pointer to a class structure, this definition attempts to get the method list entry for that method. On success a pointer to the method list structure is returned as well as the SCOPE indicator, if the method does not exist in the specified class a -1 is returned.

Default Method Actions

Any code method has a default action assigned when it is prototyped as a debugging aid. Invoking a method for which you have defined no code will give a polite message via ABORT"

: vcrash        \ ?? --
Default action for prototyped virtual methods.

: scrash        \ ?? --
Default action for prototyped static methods.

: icrash        \ ?? --
Default action for prototyped instance methods.

Method Scope Specification

During class definition the scope can be altered. These definitions are used to control/handle scoping.

: public:       \ --
During CLASS definition set the current scope to public.

: protected:    \ --
During CLASS definition set the current scope to protected.

: private:      \ --
During CLASS definition set the current scope to private.

: GetCurrentList        \ -- *list-head
Return the method list pointer for the current scope.

Name Format Checking

In order to reserve characters to provide the syntax for typecasts, scope overrides and method definition certain characters are illegal for method and class names.

Brackets are illegal, since they are used to perform typecasts.

Colon is an illegal character since it is used for scope overrides.

Period (dot) is illegal since it is used for compound invokations.

Star (*) is illegal since it declares an instance pointer.

: ?validname    \ c-addr u --
Check the name string supplied is valid for either a class or name. Causes an ABORT" on failiure.

Method Type Overrides

Methods and instance variables defined in a class can have various attributes, these are controlled by simple indicator words.

: static        \ --
Modify the global DEFFLAGS to include the static type.

: virtual       \ --
Modify the global DEFFLAGS to include the virtual type.

: post-def      \ --
Clear the global DEFFLAGS, called after a member definition to reset ready for the next member.

Data Method Prototyping

These routines are used within a CLASS or STRUCT{ definition to define data members.

: buff:         \ size "name" --
Define a data member called "name" of SIZE bytes. By default instance specific data is created, if the member was modified by the STATIC keyword, then global space is allocated. STATIC data members share the same memory location for all instances of the class and any derived classes.

: cell:         \ "name" --
A shortcut for a BUFF: of one cell.

: char:         \ "name" --
A shortcut for a BUFF: of one char.

Code Method Prototyping

These routines are used within a CLASS or STRUCT{ definition to define methods.

: static-meth:  \ "name" --
The action invoked by METH: when the STATIC modifier was present. Static members have no access to THIS or instance data and like static data members are shared between all instances of the owning class and any derived classes.

: virtual-meth: \ "name" --
The action invoked by METH: when the VIRTUAL modifier is in force. Virtual methods can be given a code definition for a class and later modified in a derived class.

: instance-meth:        \ "name" --
The default action of METH: creates a method associated with that class.

: meth:         \ "name" --
Create a code member (method). Dispatches to one of the above definitions depending on any applied modifiers.

Class Method Prototyping

These routines are used within a CLASS or STRUCT{ definition to define members which are in turn classes.

: inst:         \ *class "name" --
Embed an instance of the supplied class under the given "name".

: iptr:         \ *class "name" --
Create a typed pointer for the given class inside the current one. NOT IMPLEMENTED YET!

Operator Association

This code is used to associate an operator with a given method in a class or structure.

: FindClassOperator     \ op# *class -- *list-entry true | false
Given an operator ID and a class pointer attempt to locate the method list entry associated with that id.

: AddOperatorToClass    \ op# *list-entry *class --
Routine used to bind a method-list entry to an operator id for the given class.

: oper:         \ "name" --
Attempt to assign the method "name" as the action of the currently active operator in the current class.

CLASS Definition

Code to create CLASS definitions.

: derived?      \ "text" -- true | "" -- false
A look-ahead parsing definition used as a factor in CLASS to see if the class name is followed by a " : [<scope>] <name> " string for defining derived classes.

: derived-scope \ c-addr u -- scope flag
Another parsing definition used by CLASS, after passing the DERIVED? test the next token is checked for a scope setting. If the next token is one of "public, protected or private" then the scope id is returned and a true flag indicating the next token has been used, otherwise SCOPE_PUBLIC is assumed and a false return flag tells CLASS that this token is the actual base class name.

: class         \ "name [ : [ <scope> ] <super> ]" -- ; Exec: -- ptr
Begin a new class definition called NAME. If the new class is derived from a base class, the method lists are copied depending on C++ scoping rules, as is the Instance data size, operator chain and vtable size. When a CLASS definition is invoked it can do one of two things: If invoked within another CLASS definition it will invoke the class member instance creation (See previous section "Class Method Prototyping"), at any other time a pointer to the class structure is returned.

: end-class     \ --
Finish the definition of the current CLASS. If this is a derived class, the vtable from the parent is copied. After that any new vtable entries have the default crash vector attached.

STRUCTures - A new slant on CLASS

Under CIAO (like C++) a structure is a class. The only technical difference is that by default, members of a STRUCT{ are public.

STRUCT{ is provided for more syntactic reasons than technical. It is expected that STRUCT{ is used to declare structures consisting entirely of public data mapped in a contiguous manner so it can be be used with operating system supplied structure pointers.

: struct{       \ "name [ : <scope> <super>" -- ; Exec: -- ptr
Begin a new STRUCTure definition. Used in the same fashion as CLASS.

: }             \ --
Terminate a STRUCTure definition. See also END-CLASS.

Colon and SemiColon Override

CIAO overrides the standard Forth : and ; definitions to allow for method definitions. After a class has been defined it is necessary to write the actual code for any methods prototyped within it. CIAO like C++ takes a method name as being in the form


<class>::<method>

: ciao-colon    \ "name" -- | "name" -- "name"
The new action of : when CIAO is installed. Pre-parses the name to see if it contains a double colon. If not then the original Forth : is called. If a double colon is found the assumption is that this definition is a method. The first portion (before the ::) is taken to be a class name and the second portion the member name. The class is looked up and its *class pointer stored in a global, then the method name is looked up within that class and it's method list entry is also stored. Compilation is then triggered by :NONAME and the XT of this definition kept for use in ;

: ciao-semicolon        \ --
The CIAO over-riding action of ; to handle the closing of method definitions. If the current definition was not a method-def then only the original ; is invoked. Otherwise ; is invoked and the XT of the method is patched into the class structure depending on the method type.

: :             \ "name" --
The actual overload of Forth's :

: ;             \ --
The actual overload of the base Forth ;

OOP Compiler/Interpreter Extension Core Part 1 - EVALUATE BUFFER

This code is used by the method compiler extension in the Forth interpreter loop. It is a number of definitions used to create strings to pass to EVALUATE.

1024 buffer: CompileBuffer
The buffer used to build strings for EVALUATE.

: ResetCompileBuffer    \ --
Resets the COMPILEBUFFER for a new string.

: ciao-evaluate \ c-addr u --
All EVALUATEs for CIAO come through here.

: EvaluateCompileBuffer \ --
Pass the COMPILEBUFFER string to CIAO-EVALUATE.

: $+RCB         \ c-addr u --
Append the string in C-ADDR U to the COMPILEBUFFER.

: n+RCB         \ n --
Append the ascii representation of the number N to the COMPILEBUFFER. The string added is in the form "$<hex> "

OOP Compiler/Interpreter Extension Core Part 2 - Method Compile

These definitions handle the compilation of method-list entries depending on type.

: CompileMethod_DATA            \ *list-entry --
Compiles code for an Instance Data member. Generates a pointer by laying code for "THIS <offset> +".

: CompileMethod_STATICDATA      \ *list-entry --
Compiles code for a static data member. This is simply a literal address of the global space.

: CompileMethod_CODE            \ *list-entry --
Compiles code for an instance code member. If the member has already been bound to a definition then the definition XT is compiled along with execute (i.e. " $<XT> execute " is compiled) otherwise a pointer to where the XT will be stored is compiled with a @ execute.

: CompileMethod_STATICCODE      \ *list-entry --
Compiles code for a static code member. This is a literal address of where the XT will be and a fetch-execute.

: CompileMethod_VIRTUALCODE     \ *list-entry --
Compiles code for a virtual method. It compiles the following code.


$<virtual-method-index>            \ the index into the vtable
cells                              \ convert to vtable offset
this                               \ Get current instance pointer
cell- @                            \ Fetch objects vtable pointer
+ @                                \ extract XT from vtable
execute                            \ and run it

: CompileMethod_CLASS           \ *list-entry --
Compile code for a class instance member. This is almost identical to instance data.

: OperatorProcess               \ *class --
Compile code for any current operatortype.

: MethodTokenCompileFromList    \ *entry --
The global factor for this section. Given a method-list-entry it will dispatch to one of the COMPILEMETHOD_xxx definitions depending on type.

OOP Compiler/Interpreter Extension Core Part 3 - Single Token Check

The single-token check is used at the beginning of the Forths token interpret before the normal FIND. This is used to provide some special overrides to single Forth tokens. Namely:

1. If defining a code method, other members of the current class can be invoked simply by name. Therefore when defining a method any single token needs to be looked up in the method table before checking the normal Forth dictionary.

2. A token can be preceeded by :: which enforces that the name is searched for in global name space (the Forth dictionary) regardless. This is usually used to get you out of rule #1. If for instance you are defining a method for a class which has a member called DUMP, simply entering "DUMP" will compile a reference to that member, if you actually want to use the Forth DUMP you would type "::DUMP"

3. A token can consist of a class-name and method name separated by a double-colon. NOT IMPLEMENTED YET!. This should compile a reference to a STATIC member of a class.

: single-token  \ c-addr u -- flag
Does the single-token-check operation and returns TRUE if the token has been processed. If the token should be passed on to the normal Forth lookup FALSE is returned.

OOP Compiler/Interpreter Extension Core Part 4 - Compounds

: Process1stToken       \ c-addr u -- ap 0 | code-to-throw
Used to handle the first part of a dot-notation compound. The first 'token' needs to ultimately lay the code generate THIS and needs to locate the correct class structure pointer that begins this invocation (called the address-provider). How the first token is actually translated depends on a number of rules:

1. If defining a method, the first token may be a class or class pointer member of the current class.

2. If the first token is surrounded by brackets it's a typecast. the name in brackets identifies the address provider whilst the instance pointer is assumed to already be top of the data stack.

3. Next it could be that the first token is a named scope override. (as in rule 3 for single-token) This behaviour is NOT IMPLEMENTED YET! This is generally used for a virtual method in a derived class to invoke the action of the virtual method associated with it's parent.

4. Finally the token can either be a name in global space or a name defined as a LOCAL.

: ciao-hook     \ c-addr u -- flag
This code receives a token that has fallen through the Forth FIND and NUMBER? cycle. If the string does not contain a dot separator it is not a compound statement and false is returned. Otherwise the string is split into a heap allocated token buffer using dot as the delimiter and the following steps taken:

1. If in compile state code is laid to preserve the current THIS.

2. The first token is passed to PROCESS1STTOKEN above to obtain the instance pointer and address provider.

3. NOT IMPLEMENTED YET. The middle tokens should be processed. any of these tokens MUST represent a Class instance or class instance pointer as a member of the current address-provider. Each middle token should compile/execute code to modify THIS and the address provider each time.

4. The Final token is processed according to special rules. It must exist in the name space of the current address provider. If we are defining a method for the current address provider all three scope lists are valid, otherwise the method must be in PUBLIC name space. If successfully located according to scope rules the method entry is passed to METHODTOKENCOMPILEFROMLIST (see earlier) to compile the invokation code for that method and any applicable operator.

5. The code to restore the saved value of THIS is compiled.

Installing CIAO into VFX Forth

CIO can be installed into VFX v4 or v5 onwards.

VFX v4.x

: ciao-classhook        \ caddr len -- flag
The action of CLASSHOOK when CIAO is installed.

: ciao-undefined        \ caddr len --
The action of UNDEFINED when CIAO is installed.

: +ciao         \ --
Install CIAO's system hooks.

: -ciao         \ --
Uninstall CIAO's system hooks.

VFX v5.1 onwards

' noop  ' noop  ' postCiaoText  RecType: r:CiaoHook     \ -- struct
Contains the three actions for dot parsers.

: rec-CiaoHook  \ caddr u -- r:CiaoHook | r:fail
The parser part of the floating point recogniser.

' noop  ' noop  ' postCiaoText  RecType: r:CiaoUndef    \ -- struct
Contains the three actions for dot parsers.

: rec-CiaoUndef \ caddr u -- r:CiaoUndef | r:fail
The parser part of the floating point recogniser.

: +ciao         \ --
Switch on the CIAO parser.

: -ciao         \ --
Switch off the CIAO parser.

Instance Creation Primitives

: dynamicnew    \ *class -- this
The runtime code called for instances created via DNEW. Allocate a block of heap memory large enough for the 2 control cells (*class pointer and vtable pointer) followed by the instance data. The THIS pointer returned is the beginning of the instance data.

: staticnew     \ *class "name" -- ; Exec-child: -- this
Create a new instance of class called "name" in the dictionary. The instance is a child of CREATE which has a body containing the *CLASS value, a pointer to the instance vtable then the instance data. At runtime a THIS instance pointer is returned, this is 2 cells on from the PFA (IE past the vtable.) Forms the action of NEW when invoked outside of a : definition.

: localnew2     \ *class frame-add --
I apologise unreservedly for this trick. This definition performs the compile-time tail of local-new. Since LOCALNEW performs a create..does> I cannot add any compile time tail so I tick this definition and push it on the return stack! Sorry ;-)

: localnew      \ *class "name" --
This definition forms the action of NEW when making a local method. It hacks into VFX locals to create a new entry on the local frame and lays the code necessary (in LOCALNEW2) to setup the two control cells AT RUNTIME. The net result is a very fast and useable named local instance. Implementors beware, this is the most system specified piece of code imaginable.

Instance Creation

: dnew          \ *class -- this
State smart definition to create a new class instance on the heap. returns a pointer to the instance which can be stored. A heap allocated instance pointer can be held for as long as required and does not go "out of scope" until explicitly removed with DELETE. Use this type to create a dynamic instance which you can safely return from a method/colon definition. Note that unlike in C++ you must use DNEW in CIAO for heap allocation.

: new           \ *class "name" --
A state smart definition to create a named instance of a class. When used within a : definition the object is created in a locals frame (one is created if required). When invoked outside of a definition the instance space is ALLOTed from the dictionary. The instance goes out of scope (and is implicitly DELETEd when local) at the same time as the name goes out of scope. For a static instance, i.e. one in the dictionary, it remains in scope for the lifetime of the application (until BYE) whereas for a local instance it is DELETEd and goes out of scope at the end of the definition. Therefore please note that returning a pointer to a local instance *will* break your code - you must DNEW instead. You cannot ever explicitly DELETE a named instance. In future you will be allowed to attempt it and the effect will be to call the destructor method, as will happen when scope is lost anyway.

: delete        \ this --
DELETE is used to release the memory of a dynamic instance created via DNEW. Memory release for static and local instances is automagic. Any valid destructor is called prior to releasing the memory back to the free-heap. In future performing DELETE on a static or local will simply invoke the destructor.

AutoVar - An example of a Class

This example shows how to create and use a class called AUTOVAR. This class contains one private data member and two public methods. one method initialises the data store, the other will return the contents of that store and post-increment it.

Defining the class


class AutoVar           \ begin a new class definition
private:
        cell:   m_data  \ Where the count will be stored
public:
        meth:   read++  \ The method to read and increment
        meth:   set     \ The method to initialise the count
end-class               \ end definition

Coding the Methods


: AutoVar::read++       \ -- n ; Method
  1 m_data dup @ -rot +!        \ read and increment data store
;

: AutoVar::set          \ n -- ; Method
  m_data !                      \ write to data store
;

Test Code

Here are three test routines which each take an initial value for an AutoVar type and then run the read++ method 10 times writing the result. The first case uses a static instance of AutoVar, the second case uses a local instance, and finally the third case shows how to use a heap allocated instance and how to typecast an object pointer. Note that in CIAO there are separate words for static/local instances with NEW and heap allocation with DNEW.


AutoVar new Foo         \ create a static instance of Autovar

: test1                 \ n -- ; Test static instance FOO
  foo.set                       \ init with supplied index
  10 0 do
    cr foo.read++ .             \ read and increment 10 times!
  loop
;

: test2                 \ n -- ; Same thing, local class tho'

  AutoVar new Foobar            \ create local instance of AUTOVAR

  foobar.set
  10 0 do
    cr foobar.read++ .
  loop
;

: test3                 \ n -- ; Third time, heap allocated instance

  AutoVar dnew                  \ create instance and store pointer

  tuck (AutoVar).set            \ use type cast on heap pointer    )
  10 0 do
    cr dup (AutoVar).read++ .                                      )
  loop

  delete                        \ destroy heap instance
;

AutoVar2 - Another Example

AUTOVAR2 is a class derived from AUTOVAR to extend its functionality. AutoVar2 has no publically accessable methods but uses operators to perform the read and initialise.


Class AutoVar2 : private Autovar   \ Create new class, inherit from
                                   \ Autovar with all methods in
                                   \ private scope.
     oper: read++                  \ Default operator performs
                                   \ read++ method
  to oper: set                     \ TO operator performs set method
end-class

The test procedures could now look like:


AutoVar2 new Foo         \ create a static instance

: test1                 \ n -- ; Test static instance FOO
  to Foo                        \ init with supplied index
  10 0 do
    cr foo .                    \ read and increment 10 times!
  loop
;

: test2                 \ n -- ; Same thing, local class tho'

  AutoVar2 new Foobar            \ create local instance

  to Foobar
  10 0 do
    cr foobar .
  loop
;

Class Library

The following code documents the beginning of an MFC style class library for CIAO.

Base Operators

The following operators have been defined and are used through out the base classes whenever applicable. Each class will document its use of these operators.

operator: ++                    \ --
Increment by 1

operator: --                    \ --
Decrement by 1

operator: cout<<                \ --
Display applicable output

operator: cout<<hex             \ --
Display applicable output in hex

operator: xywh->                \ x y width height --
Store X Y WIDTH HEIGHT parameters.

operator: lprect->              \ *Rect --
Store X Y WIDTH HEIGHT obtained from a RECT structure.

operator: []                    \ index -- char
Get array element at index.

operator: []to                  \ index val --
Set element at index: <idx> <val> []to <class>

operator: (LPCTSTR)             \ -- z$
Get contents as a zero-termintated string.

operator: (LPCTSTR)to           \ z$ --
Set contents from a 0 terminated string pointer.

operator: +=                    \ instance-pointer --
Concatenate/Add from a class of same type.

operator: (LPCTSTR)+=           \ z$ --
Concatenate from a 0 terminated string.

Primitive Types

This collection of classes represents the primitive data types found in C++. They can be though of as extended Forth VALUEs.

INT

This data type represents a simple number which is basically equivalent to a CELL. It has no methods publically callable but simply uses operators to access.

The following operators have been assigned to methods for this class.

<default>

No operator, returns contents.

to

Set content from stack item.

addr

Get the address rather than contents.

++

Increment by 1.

--

Decrement by 1.

cout<<

Write contents to console.

cout<<hex

Write contents as hex to console.

Windows Types

Defines some simple classes for Windows data-types. Most simple types under Windows are just 32 bit numbers. Therefore the following types are all simply private scope derived from the INT type documented before.


LPVOID             HANDLE          HWND            HMENU
HINSTANCE          LPCTSTR         LONG            DWORD

Windows Structures

The following structures are defined using standard Windows names. all data members are one of the previously defined Windows types.


RECT       CREATESTRUCT            POINT

CPOINT - Point Class

This is simply a class version of the POINT structure. The reason for the separation is that the STRUCT{ version can be typecast from an OS supplied point-struct into a CIAO POINT struct or CPOINT class

The CPOINT class is publicly derived from POINT with a method and operator for TO supplied which takes another point as the source.

CRECT - Rect Class

This is a class version of the RECT structure. The CRECT class is publicly derived from RECT with methods and operators.


class CRect : public Rect
public:
                   meth:   Width
                   meth:   Height
                   meth:   SetXYWH
                   meth:   SetLPRECT
                   meth:   dump
   ->              oper:   SetLPRect
   xywh->          oper:   SetXYWH
   lprect->        oper:   SetLPRECT
   cout<<          oper:   dump

CString - Dynamic String Class

A CString object contains a variable-length sequence of characters. It also provides functions and operators which allow for easy to Concatenation and comparison operators, together with automatic memory management. CString objects are far easier to use than ordinary character arrays.

CString is based on the FORTH char data type.

CString Objects have the following useful characteristics:

Variable Type Arguments

Some of the members of this class can take either a character or a zero-terminated string as a parameter. This is autodetected by the simple assumption that any value greater than the maximum value storable in a char is an array pointer. For 8 bit character systems this means a pointer cannot be in the range 0..255.

The CString Class Members

: CString::ResizeBuffer \ rsize --
Resize the current string buffer memory to RSIZE chars.

: CString::Empty        \ --
Release all string memory and return to init state.

: CString::GetAt        \ idx -- char
Return the ascii character at IDX position in the string.

: CString::GetLength    \ -- n
Return the length of the current string.

: CString::GetLPCTSTR   \ -- z$
Return a pointer to the string as a zero-terminated. After obtaining this pointer, any operation which modifies the string may destroy this pointer.

: CString::IsEmpty      \ -- BOOL
Return TRUE if there is no string information.

: CString::SetAt        \ idx char --
Place character CHAR at the IDX position in the string.

: CString::Add          \ *CString --
Add the contents of the CString class pointed to onto the end of the current string.

: CString::AddLPCTSTR   \ z$ --
Add the supplied zero terminated string to the end of the current.

: CString::SetLPCTSTR   \ z$ --
Replace the current string with the supplied zero terminated one.

: CString::to           \ *CString --
Replace the current string with the contents of the CString class whose pointer is supplied.

: CString::Compare      \ z$ -- flag
Compare the current string with the zero-terminated string supplied.

: CString::CompareNoCase \ z$ -- flag
As CString::Compare except character case is ignored.

: CString::Mid          \ first count -- *CString(dynamic)
Return a new dynamic instance pointer for a CString which contains a substring of the current. COUNT characters from index FIRST are copied.

: CString::Left         \ count -- *CString(dynamic)
Return a new dynamic instance pointer for a CString which contains a substring of the current. COUNT characters are copied from the start (left) of the string.

: CString::Right        \ count -- *CString(dynamic)
Return a new dynamic instance pointer for a CString which contains a substring of the current. COUNT characters are copied from the end (right) of the string.

: CString::Delete       \ index count -- newlen
Remove COUNT characters from the string starting at the INDEX position. If count+index exceeds the string length it is trucated. Also returns the new length of the string after the delete.

: CString::Insert       \ index z$ -- int | index char -- int
Passed either an index and a z$ or an index and a character this will perform an insert operation. The string or character supplied is inserted starting at the original offset INDEX. Returns the new length of the string.

: CString::MakeUpper    \ --
Convert the classes string data to upper case where possible.

: CString::MakeLower    \ --
Convert the classes string data to lower case where possible.

Operator Associations

The following operators have been assigned to methods for this class.

[]

GetAt -- Get character at specified index.

[]to

SetAt -- Set chatacter at specified index.

(LPCTSTR)

GetLPCTSTR -- Return 0terminated string pointer. )

to

to -- Assign from another CString.

(LPCTSTR)to

SetLPCTSTR -- Assign from a 0 terminated string. )

+=

Add -- Append from another CString.

(LPCTSTR)+=

AddLPCTSTR -- Append from a 0 terminated string. )