Developer's Guide to iCL

The iMatix Class Language

Introduction

In which we explain why we felt it necessary to invent yet another way of writing code, and in which we introduce some of the main characters in the drama.

Purpose, philosophy and principles

iCL is a software development methodology and toolkit aimed at constructing very large and very high-quality applications in ANSI C. We use C in infrastructure projects because it is portable, fast and operationally stable. However, C lacks modern facilities such as:

  1. inheritance (where functionality can be added by extending and building on existing components)
  2. templating (where variations on a standard model can be produced cheaply)
  3. API standardization (where the structure of APIs is enforced by the language).
  4. literate coding (where documentation can be written together with the code)
  5. logical modelling (where we define abstract models such as finite state machines directly in the code)

These can be done manually, and usually are, but that is expensive and demanding.

These facilities are helpful because they allow us to work faster and with better results. We can make larger and more ambitious designs with less risk of losing control over them. iCL adds these facilities to C by wrapping it into a higher level class library (1 - 3) which is self-descriptive (4), simple, extensible and language independent. iCL adds a stage to the deployment process, but unlike interpreted paradigms, portability and efficiency are guaranteed as an atomic quality that follows as a natural consequence of the target language, ANSI C. Our solution aims to leverage skills which are normally part of the essential training of any IT professional (C, XML).

We write our programs as iCL classes, using a simple XML language. The XML language (called "iCL") is designed to be easy to read and write by hand - no special editors are needed. An iCL class consists mainly of a set of properties and a set of methods. The properties define an object's state. The methods define an object's functionality.

iCL classes compile into C code, which we then compile and link as usual. In a best-case scenario, we can see up to a 10-to-1 ratio between the final C code and the hand-written iCL classes, but coding effort compression is typically non-negligible. The generated C code is perhaps 20-30% larger than a comparable hand-written application, but we typically get less complex and highly optimized code.

iCL does not attempt to create a full OO model. It aims to remove much of the administrative burden from writing large-scale C applications, to produce high-quality code (5), and to ensure that certain problems - such as memory management - can be totally abstracted and thus solved "correctly" once and for all.

Dependencies

iCL is part of the iMatix tool chain (the "Technology Packages"), and specifically part of the package called Base/2. It depends on Base/1 and GSL/4 - you must download and install these before starting with iCL. See http://www.imatix.net/pub.

Base/2 uses the Apache Runtime library (APR) for certain functions, and iCL applications may make calls to this library. APR is packaged with Base/2; you do not need to take any special action to get APR.

iCL is part of the Base/2 toolchain: we built it using the XNF meta-generator (a tool that turns abstracted grammars into code generators), and iCL forms the basis for our portability library (iPR), which you will want to use in your iCL applications since it provides generally-useful classes.

Copyrights and credits

iCL is copyright (c) 2004-2007 iMatix Corporation, and is licensed as free software under the GPL, with a commercial opt-out option.

iCL was designed and written by Pieter Hintjens and Jonathan Schultz, with suggestions from Ewen McNeill, Martin Lucina, and others. The concepts in iCL are derived from many other iMatix code generation frameworks including iAF (1998), Boom (2002), and Aqua (2003).

Features

The basic features of iCL are:

  • multiple inheritance: we construct classes by extending existing classes.
  • templating: we construct complex data types - such as hashes and lists - by inheriting from base classes.
  • literate coding: we write the documentation for a class together with the code for that class.
  • logical modelling: we can extend the class language to support abstract models such as finite state machines, and we can generate implementations for these models directly into the final output code.
  • API standardization: the syntax and semantics of all APIs conform to the best modern practice.
  • self-testing: we embed self-test code directly into the class, and iCL generates self-test programs automatically.

Concepts

Source files

iCL source files have the extension ".icl". You edit these using a normal text editor, ensuring that the files keep a correct iCL syntax. This is a matter of using the correct element tags and writing valid XML.

One class equals one file. We can split large classes into smaller pieces but we do this by creating more classes, not breaking one file into many files. We do not put multiple classes into one source file.

This makes it easy to know what goes where. The file name (sans the .icl extension) is the same as the class name (with underscores replacing spaces and other non-alphanumeric characters).

Types of class

A "stateful" class is one that is instantiated as a set of objects, each holding a set of properties. The class provides methods which perform some work, often using the properties. Stateful classes conventionally use two methods - new and destroy - to create and destroy objects.

A "stateless" class is one that consists only of methods, without a new or destroy method. Stateless classes are not instantiated as objects. They correspond closely to a standard C "module", i.e. a set of functions.

An "abstract" class is one that provides a set of properties and methods for use by other classes. Abstract classes are not used directly, only through inheritance. The "abstract" title really just means that the class is not turned into C code.

An "allocator" class is an abstract class that provides memory management for a stateful class. Basically, implementing the new and destroy methods in a more or less intelligent way.

We build all stateful classes from an allocator class (icl_alloc_cache or icl_alloc_plain). This can happen directly, or by inheriting from classes that themselves inherit from the allocator class.

We usually build stateless classes on top of icl_base.

Substitution

The $(selfname) symbol can be used in the iCL code and is replaced by the name of the class. The $(selftype) symbol is replaced by the typedef for the class (the name of the class followed by _t).

Self

In methods that operate on a specific object (in stateful classes) we use the name "self" to refer to the object. This is always the first argument of the method.

We pass classes by reference. "self" is always a pointer to $(selftype).

item = self_new ();
self_print (item);

Local methods

A class can refer to its own methods using the self_ prefix:

<method name = "selftest">
    <local>
    $(selftype)
        *item;
    </local>
    item = self_new ("Depaul", "New Street");
    self_destroy (&item);
</method>

Context

A stateful class can have a set of properties, which we call "context". Properties are C data types, and can be (references to) iCL objects. The properties are specified as a block of C code that is copied literally (with substitution) into the generated C code:

<context>
    char *name;
    char *address;
</context>

Methods

The iCL base classes provide templates for common types of method. You can define your own templates. iCL uses the method name to control inheritance - thus you can extend methods such as "new" simply by defining them in your class, with additional code:

<method name = "new">
    <argument name = "name"    type = "char *" />
    <argument name = "address" type = "char *" />
    self->name    = icl_mem_strdup (name);
    self->address = icl_mem_strdup (address);
</method>

<method name = "destroy">
    icl_mem_free (self->name);
    icl_mem_free (self->address);
</method>

All methods are public. To define private functionality, use the <private> block to define local static functions.

Inheritance

The code generation process is driven by inheritance. A class may specify one or more classes to inherit from, using the <inherit> tag. These are inherited in order. The full contents of the parent class are copied and merged with the current class. This merge process is simple for most elements: the parent elements are inserted before the child elements. For methods, the process becomes more complex - we call this layering.

<class ...>
<inherit class = "parent class" />
</class>

Layering

In general, inherited elements are inserted before the elements of the class being constructed. For methods the writer of the parent class can explicitly layer the parent code before or after the child code using two tags, <header> and <footer>. For example:

<method name = "new">
    <header>
    self = s_$(selfname)_alloc ();
    if (self) {
    </header>
    <footer>
    }
    </footer>
</method>

Self-testing

As a principle we aim to make all classes self-testing. That is, each class contains its own testing code. (We have observed that test programs which are maintained separately tend to be poorly maintained.)

The self test code is written as a method called "selftest":

<method name = "selftest">
    ...
</method>

Local data

A method that needs local data uses the <local> tag to encapsulate this:

<local>
$(selftype)
    *self;
int
    count;
</local>

Argument precalculation

It is useful to be able to pass compiler macros to methods, especially when tracking memory allocation (so that memory leaks can be traced to the correct place in the application).

iCL supports this as follows:

<method name = "new">
    <argument name = "file" type = "char *" precalc = "__FILE__" />
    <argument name = "line" type = "size_t" precalc = "__LINE__" />
    ...
</method>

When a method has precalculated arguments, iCL mangles the name of the method by adding an underscore, and generates a macro with the method name to implement the precalculated arguments.

Private and public blocks

A class is compiled into a C source file and a corresponding header file. You can inject definitions into these two files using the public and private elements:

<public>
... definitions available to all users of the class
</public>

<private>
... definitions used only by the class properties and methods
</private>

The public and private tags can be named to control exactly where they are placed in the generated code. See the iCL reference for more details.

Importing a class

When one class wants to use another, it uses the <import> tag:

<import class = "icl_mem" />

While inheriting a class tells iCL "I want to take this class and make my own extended version of it," importing a class tells iCL, "I want to use the methods and public definitions that this class provides."

When you import a class you automatically import all the classes it imports. A class can import itself, indirectly, and the order of imports is not significant.

C class implementation

iCL does not use much magic in its generated code. In fact the C code is intended to look very much like normal hand-written C, except that it is more consistent and complete than a human programmer could make it, and is rich in assertions and debugging and tracing code.

Methods are implemented as C functions. Objects are implemented as structures of a fixed size. Variable data is allocated in the new method and freed in the destroy method.

Each class is generated into a source (.c) and a header (.h) file.

iCL uses a multi-pass #include strategy to ensure that class references are not dependent on some order. I.e. classes can import each other, and even themselves, with no special effort on behalf of the developer.

This does make the generated headers rather complex. We have not found a better solution despite some valiant efforts.

Object properties

Object properties are generally public. There is no convention for private properties, though it is possible in C to create private structures. It is highly convenient and efficient to be able to inspect (and modify) an object's properties directly rather than have to work through methods.

This does create a maintenance burden in some cases. The solution we recommend is that (a) methods be provided for any sophisticated or abstractable manipulation of an object's properties, and (b) the context block be well-documented so that it can be understood and used clearly.

At a later stage we may foresee support for private properties but as the current design seems to work well, we are not in a hurry to change this.

Conventions

Naming conventions

  • We use lower-case spelling for all names, and underscores (or spaces) to separate words.
  • The type of a class is its name followed by "_t".
  • We name classes using a project prefix, followed by a class name. For instance, icl_mem.
  • Commonly-used classes should have shorter names.
  • The internal name of the class is the same as its filename. A single iCL file contains a single class.
  • Method function names are constructed from the class name, followed by an underscore, and the method name. E.g. the "alloc" method in icl_mem is called "icl_mem_alloc".
  • Local functions and static variables are prefixed by "s_".

Legible names

iCL allows and encourages the use of names with spaces, for classes, for arguments, declarations, and methods. These names are mangled into valid C names by replacing the spaces with underlines. Thus these two method names are equivalent: "first item", and "first_item". Hyphens and other characters that are not valid in C names are also turned into underscores.

Constructors and destructors

Constructors and destructors are only implemented in stateful classes that are (indirectly) inherited from icl_object.

The constructor is called "new" and returns a new object reference. The object is always nullified unless the class explicitly overrides this (using a rule - see the iCL reference). If the constructor failed, it returns NULL. A constructor can be very complex, creating other objects. Internally, any failure is handled by calling self_destroy, which must be "safe" in the sense that it does not assume a fully-constructed object. The constructor can take arguments, used in initialising the object. A constructed object is "ready for use".

The destructor is called "destroy" and always takes a pointer to an object reference ($(selftype) **). The template destructor dereferences this to provide a "self" for use in the destructor code. After destruction the provided object reference is nullified. A destructor may also decide to do nothing - e.g. reference counting affects the destructor.

Method return values

In general, methods signal success with a zero return value and failure by a non-zero return value. Methods that return object references do the opposite - they signal success by returning an object reference and failure by returning NULL.

The "function" template

The most common type of method in a stateful class accepts an object reference along with some arguments, does some work and returns a success/failure indicator. This common case is implemented as a template called "function".

iCL syntax and style

The conventional style for iCL XML code can be seen by studying the iCL classes. In summary:

  • all elements under <class> are aligned at the left margin.
  • short elements are defined as a single line.
  • elements with content are defined as a multi-line block.
  • single blank lines separate multi-line elements.
  • element content is indented four spaces.
  • when an element is repeated multiple times, the attributes are aligned to make the text easier to read.

iCL Classes

The current and accurate documentation for iCL classes is the source code of the classes themselves.

Using iCL

In which we try to empathize with someone seeing iCL for the first time, and probably fail totally.

Getting started

A good way to start with any new tool is to make a new directory, check that your controls are working - i.e. that you can run the commands you need - and then to make a trivial test.

Let's start by checking that gsl works:

gsl

Next, let's check that iCL was correctly installed:

gsl icl_base.icl

That should show a warning like "WARNING: Abstract class - no code generated." Now, let's make a trivial test class ("my_class.icl") and build and test it:

<?xml?>
<class
    name    = "my_class"
    script  = "icl_gen"
    animate = "1"
    >
<inherit class = "icl_object">
    <option name = "alloc" value = "cache" />
</inherit>
<context>
    int identifier;
</context>
<method name = "new">
    <argument name = "identifier" type = "int" />
    self->identifier = identifier;
</method>
<method name = "selftest">
    <local>
    $(selftype)
        *item;
    </local>
    item = self_new (1);
    self_destroy (&item);
</method>
</class>

Note that the <?xml?> and <class> tags are not indented. To build the class, and test it:

gsl my_class.icl
c my_class
c -r libtest my_class
c -L my_class_test
my_class_test

We've asked for "animation", so the class prints out each method as it runs:

my_class: <selftest>
my_class: <new file="my_class.c" line="85" identifier="1">
my_class: </new file="my_class.c" line="85" identifier="1" self="0x8058e00">
my_class: <destroy self_p="0xbffff794">
my_class: </destroy self_p="0xbffff794" self="0x8058e00">
my_class: </selftest>

When I wrote this test class, I got it wrong the first time. First, I did not define a <context> block. Without this, the class is stateless. Fine, but the new and destroy methods won't compile - they look for a structure called "my_class_t", and this structure is only built if the context block is present.

Secondly, I defined the <argument> with no type attribute. This happens to be legal (so you can use names like "char somename []"), but generates invalid code.

The lesson here is: while iCL does check many errors, it lets you construct invalid C code. If you get compilation errors, check the generated code: it is intended to be readable.

Next, we're going to make a boom project to automate the commands we typed to build to class. For one class it's fine - but for an application it is nonsensical to do this by hand. Here's the minimal project:

<?xml version="1.0"?>
<pdl name = "scratch" version = "0.1" >
    <inherit filename = "classes.pdl"     />
    <inherit filename = "icl_classes.pdl" />
    <file name = "my_class.icl" class = "icl private class" />
</pdl>

And this is how we do a full rebuild:

boom all

Which, if it works, tells us this:

boom I: configuring scratch/0.1...
boom I: Generating files...
boom I: Building application...
boom I: Installing scratch into /home/ph...

Incidentally, if you're wondering why we have boom rather than using make or one of the dozens of other build tools, run this command:

boom distsrc

and take a close look at the files this produces. By defining an abstract project we get more than just build scripts, we also get complete deliverable packages.

Warming up

Here is the basic guide to learning anything:

  1. Start simple.
  2. Repeat often and with small gradual improvements.

And so it is with iCL. Repetition makes perfect.

Any realistic iCL application will make heavy use of the iPR classes, so we suggest you read the iPR documentation and browse the classes. To make life easy, all classes come with demo/test programs. To use a class, look at the test code and copy/paste the pieces that look useful.

Class options

Class options are settings that control different aspects of the code generation process. Different base classes can define the options they use - the documentation for each class tells you what options it handles and what they do.

Here is a list of the common and useful options:

<option name = "selftest" value = "0" />

Disables the self testing functionality. By default all classes have a self-test program, unless they are abstract classes with no generated output.

<option name = "nullify" value = "0" />

Disables the nullification of new objects. By default this is always done but in the case of very large objects, you may prefer to disable it and nullify individual fields only, as a performance optimization.

Class design

Stateless classes

Build stateless classes to encapsulate basic functionality. You may find that some problems do not merit an "object oriented" approach, while others lend themselves naturally to that. It can be very tedious to - e.g. have to create an object just to do an operation like delete a file. But when designing large structures, objects are often elegant.

Here is a trivial example of a stateless class:

<?xml?>
<class name = "math" script = "icl_gen">
<inherit class = "icl_base" />

<method name = "area" return = "value">
    <argument name = "radius" type = "double" />
    <declare name = "value" type = "double" />
    value = 2 * radius * self_pi ();
</method>

<method name = "pi" return = "value">
    <declare name = "value" type = "double" />
    value = 3;          //  Very approximately :-)
</method>

<method name = "selftest">
    assert (math_area (1) == 2 * math_pi ());
</method>
</class>

Data structures

iPR provides a number of basic data structures - lists, hashes, arrays. You may find these useful, or you may build your own. The value of these structures is that you use them as templates for your own classes.

For example, a list consists of a list header class (the "container") and a list item class. When you create your own list class, it likewise has a container and an item class. Usually the container class is generated automatically.

It gets interesting when you add your own functionality (in the form of properties and methods) to the item and container classes. Thus, you may have a list container with basic methods such as "first", "next", etc. You can add your own, e.g. "print", "search".

Cascading classes

It is often useful to design your application as a cascade of classes where each class encapsulates a set of other classes. This is nothing special - hierarchical data structures are an old concept.

Use the new and destroy methods to build-up and tear-down these child classes. This means you can create and destroy objects at any level without knowing what dependencies they have.

Putting intelligence in the right place

As a general rule, it is always better to let objects work on themselves. I.e. if you find yourself writing code that modifies or searches for data in some object, that code should probably be a method in the object class or its container.

Frequently-asked questions

Why are '\' characters removed from my code?

Code blocks - such as method bodies and public or private definitions - are treated as GSL script text. This means that you must escape any characters that have special meaning to GSL. To insert a back-slash, use \\. Typically this hits when you define macros, or use '\0' in string manipulation. You can avoid the latter error by just using a zero.

Why does iCL not recognise the <?xml?> tag?

Don't put spaces or blank lines before this tag - it must come at the start of the file to have the desired effect of marking the file as XML.

Frequently-regretted mistakes

Inaccurate abstractions

Creating the right design to solve a problem is hard work, and no tools can replace the effort and experience needed. Objects are not a magic alternative to understanding.

There are some techniques that can help:

  1. Look at every class as something you are producing for someone else. Is the interface clear? Does it solve a problem that is obvious and easy to explain? Do you feel comfortable giving this to other people? If not, improve the design or start again.
  2. Put your effort in the places where it's most important. No software is perfect all the way through. Use simple (if inefficient) solutions and then improve the areas where there is an obvious need.
  3. Aim to build a first version rapidly, even if it is incomplete. Do not try to make a perfect design the first time - it will not work and you will find yourself throwing out a design that is highly-polished but fundamentally flawed.
  4. Assuming you will throw-away the first version you make, try at least to wrap as much as you can in clear and reusable classes. This demands a minimalistic approach - every dependency in a class makes it less likely to survive a design revision.
  5. If possible, make classes that solve general problems, not specific ones. Imagine that other people will use your classes for unrelated work. Try to make that happen - it will improve your design and often make your work go faster and better.

Over-complex classes

A class can easily grow too large, with methods that are over-complex. This is a sign that the class needs redesign, breaking into smaller pieces. If you have been careful, you can do this without modifying code that uses the class.

Getting technical

Using GSL in code blocks

Wherever you write C code in your iCL class, you are in fact writing GSL templates. This is why symbols like $(selfname) expand into the class name. This is most useful when writing base classes where you want to generate code conditionally.

For example, the allocator classes nullify new objects if the class option "nullify" is set:

.if (class->option (name = "nullify").value?1) = 1
    memset (item, 0, sizeof ($(selftype)));
.endif

(Note that the dot starts at the left margin).

Here is how a hash table class decides how large to make its hash tables:

<public>
.if class->option (name = "bigtable").value ?= "1"
#define $(childname:upper)_TABLE_MAXSIZE 65535
.else
#define $(childname:upper)_TABLE_MAXSIZE 255
.endif
</public>

To keep things simple, don't use GSL constructs in your application classes, since they make the code harder to read and maintain.

Mixing iCL and non-iCL code

Since iCL is simple C you can call arbitrary C from iCL applications, and vice-versa.

Mixing iCL header files with non-iCL header files is sometimes delicate due to the way iCL resolves class importation.

The problem stems from the fact that iCL includes all classes twice, once to collect "safe" definitions, and once to collect "unsafe" definitions which depend on the safe ones. Look at any class header file for an example.

It is standard practice in C header files to use conditional inclusion like this:

#ifndef HAVE_THIS_FILE
#define HAVE_THIS_FILE
...
#endif

You must not attempt to include an iCL header file inside such an #if block.

This will cause many compile errors, since the unsafe half of the iCL header will never be included.

Creating a project header file

It is often useful to create a single header file that includes all header files exported by a project. This simplifies work for the calling programs, who do not need to know about individual include files. (The C preprocessor does more work.)

When building such a project header file, do not use conditional inclusion. If you have hand-written header files, you should protect these internally using conditional inclusion.

Here is a prototypical project header file:

/*  Comments and copyright                      */

/*  Hand-built header files                     */
#include "header1.h"
#include "header2.h"

/*  Public classes                              */
#include "class1.h"
#include "class2.h"

Reference Section

Summary of the iCL language

This summary shows the hierarchy of elements you can use, with the required and optional attributes for each element. The XML entity and attribute names are case-sensitive and we use only lower-case names.

<class version name [role] [after] [copyright] [license] [before] [comment] [abstract]
     [animate] [import] [target] [base] [trace]>
   <inherit name [phase] [condition]>
      <option name value/>
   </inherit>
   <data [name]/>
   <invoke [script] [phase]/>
   <public [name]>
      <doc [domain]/>
   </public>
   <private [name]>
      <doc .../>
   </private>
   <context [export]>
      <doc .../>
   </context>
   <doc .../>
   <import class [condition]/>
   <assert role/>
   <option .../>
   <method name [template] [abstract] [condition] [inherit] [export] [private] [cname]
        [base] [return] [inline] [trace]>
      <option .../>
      <inherit .../>
      <argument name [condition] [type] [pass] [index] [default] [precalc] [animate] [ref] [export]>
         <doc .../>
      </argument>
      <animate format name [value] [condition]/>
      <dismiss argument value/>
      <declare type name [condition] [default] [precond] [animate]/>
      <local>
         <doc .../>
      </local>
      <header [export]/>
      <footer [export]/>
      <doc .../>
      <invoke .../>
   </method>
   <todo [owner]/>
</class>

Detailed specifications

All child entities are optional and can occur zero or more times without any specific limits unless otherwise specified. The same tag may occur at different levels with different meanings, and in such cases will be detailed more than once here.

The 'icl' item

The class tag defines the class. One iCL file defines exactly one class.

<class
    version = "..."
    name = "..."
  [ role = "..." ]
  [ after = "..." ]
  [ copyright = "..." ]
  [ license = "bsd | gpl"  ("gpl") ]
  [ before = "..." ]
  [ comment = "..." ]
  [ abstract = "0 | 1"  ("0") ]
  [ animate = "0 | 1"  ("$(switches.animate?0)") ]
  [ import = "0 | 1"  ("0") ]
  [ target = "doc | stdc | stdcpp"  ("stdc") ]
  [ base = "..."  ("$(name)") ]
  [ trace = "..."  ("NULL") ]
    >
    <inherit>
    <data>
    <invoke>
    <public>
    <private>
    <context>
    <doc>
    <import>
    <assert>
    <option>
    <method>
    <todo>
</class>

The icl item can have these attributes:

abstract
If set, the entity only exists in order to be inherited - no code is generated. The abstract attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 normal entity
1 abstract entity

role
A file may fulfill a role. This serves two purposes: asserting that essential roles are fulfilled and helping to define the inheritence order using the 'before' and 'after' attributes. The role attribute is optional.
before
Specifies a role before which this file should should be inherited. The before attribute is optional.
after
Specifies a role before which this file should should be inherited. The after attribute is optional.
copyright
This specifies the copyright string for the model. This string is stamped into the generated sources, if specified. The copyright attribute is optional.
license
Specifies the license of the model. This license is applied to all models inherited or built from the current model. The license attribute is optional. Its default value is "gpl". It can take one of the following values:
Value Meaning
bsd generates a BSD license header
gpl generates a GPL license header

name
Specifies the name of the class. This name will be used to prefix all function names and will also be used as the filename for generated code. The name attribute is required.
comment
An optional one-line comment that describes the class. The comment attribute is optional.
version
Specifies the version of the class. This text can take any format but we recommend this standard format: '2.4b1' which is major version 2, minor version 4, release b, update 1. This string is stamped into the project sources. The version attribute is required.
animate
If set, the generated code contains animation that can be switched on and off at runtime. This option can be overridden by a command-line switch (e.g. "-animate:0"). The animate option can be inherited from a parent class. The animate attribute is optional. Its default value is "$(switches.animate?0)". It can take one of the following values:
Value Meaning
0 do not animate
1 generate animation code

import
If set, the class is only a collection of imports of other classes. The import attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 normal class
1 import-only class

target
Specifies the name of the target environment; the target is implemented by a GSL script that generates code for a specific language environment. The target can be inherited from a parent class. Specifies the name of the target environment; the target is implemented by a GSL script that generates code for a specific language environment. The target can be inherited from a parent class. The target attribute is optional. Its default value is "stdc". It can take one of the following values:
Value Meaning
doc Documentation
stdc Standard ANSI C + iMatix runtime
stdcpp Standard ANSI C++ wrapper

base
Generated functions usually begin with the class name. This can be over-ridden by the use of the attribute 'base', which can be set to empty. The base attribute is optional. Its default value is "$(name)".
trace
Specifies which trace object to use; default means global trace; 0 means no trace. The trace attribute is optional. Its default value is "NULL".

The 'inherit' item

<inherit
    name = "..."
  [ phase = "preproc | parse" ]
  [ condition = "..." ]
    >
    <option>
</inherit>

The inherit item can have these attributes:

name
Name of entity to inherit from. The name attribute is required.
phase
The processing phase during which this inheritence is performed. The phase attribute is optional. It can take one of the following values:
Value Meaning
preproc inherited in preprocessor
parse inherited in parser

condition
Specifies a condition which must be TRUE for the inheritence to occur. The condition attribute is optional.

The 'option' item

Passes an option to an inherited class. Options can be used in the template code generation logic, or in method handlers.

<option
    name = "..."
    value = "..."
    />

The option item can have these attributes:

name
The name of the option. The name attribute is required.
value
The value for the option. The value attribute is required.

The 'data' item

Holds a block of arbitrary XML data, for use by invoke scripts. The data blocks' grammar is defined by implicit accord with the invoked scripts.

<data
  [ name = "..." ]
    />

The data item has this single attribute:

name
The name attribute is optional.

The 'invoke' item

Invoke gsl code to operate on the class tree. Invokes the gsl code contained in the invoke item body, if any, followed by the gsl code specified by the script attribute, if present. The gsl code can access the class entity and manipulate it in any way desired. It may also generate other files; the script is invoked before any other code generation starts. The invoke tag can contain arbitrary XML definitions for use by the gsl script. Note that while method templates are resolved _after_ all invoke tags, class inheritence is done before, and the invoked script cannot affect it.

<invoke
  [ script = "..." ]
  [ phase = "preproc | parse" ]
    />

The invoke item can have these attributes:

script
The name of the GSL script, without any extension (.gsl is enforced). The script attribute is optional.
phase
The processing phase during which this invoke is executed. The phase attribute is optional. It can take one of the following values:
Value Meaning
preproc rule is used in preprocessor
parse rule is used in parser

The 'public' item

Public definitions, exported for use by callers of the class. In C, these definitions are copied into the class header file. Public definitions are inherited from the parent classes unless you specify inherit = "0". If the definitions are named, each named block is indepedently inherited.

<public
  [ name = "header | include | types | structure | functions | inline | footer"  ("types") ]
    >
    <doc>
</public>

The public item has this single attribute:

name
The name of the public block, which really means the place in the generated code that this public block shoud be inserted. The name attribute is optional. Its default value is "types". It can take one of the following values:
Value Meaning
header issued before class imports, in first pass
include issued after class imports, in each pass
types for type definitions
structure after structure definition
functions for functions prototypes
inline for inline functions
footer issued at end of file

The 'doc' item

Documentation for the current element: this is included in the generated source code in a suitable form. Documentation should be in iMatix gurudoc format.

<doc
  [ domain = "..." ]
    />

The doc item has this single attribute:

domain
Allows documentation of different types to be included in the iCL definitions. The domain attribute is optional.

The 'private' item

Private definitions, used by the class itself. The private definitions can include static variables and local functions. Private definitions are inherited from the parent classes unless you specify inherit = "0". If the definitions are named, each named block is indepedently inherited.

<private
  [ name = "defines | header | body | footer"  ("body") ]
    >
    <doc>
</private>

The private item has this single attribute:

name
The name of the private block, can be "header" to hint the code generator to place this at the top of the generated file. The name attribute is optional. Its default value is "body". It can take one of the following values:
Value Meaning
defines issued before class header
header issued after class header
body issued in middle of source
footer issued at end of source

The 'context' item

Defines a context block; one or more variables which will be held in all class instances.

<context
  [ export = "none | default | before | after"  ("default") ]
    >
    <doc>
</context>

The context item has this single attribute:

export
Specifies how this item may be inherited. The export attribute is optional. Its default value is "default". It can take one of the following values:
Value Meaning
none may not be inherited
default inherited in the default manner
before inherited before existing items
after inherited after existing items

The 'import' item

Specifies other classes that this class refers to. Note if you want the generated code to be correct you must define an import item for each class that you refer to in your class context or methods. By default, the import tag is inherited unless you specify inherit = "0".

<import
    class = "..."
  [ condition = "..." ]
    />

The import item can have these attributes:

condition
Specifies a condition which must be TRUE for the entity to be created. The condition attribute is optional.
class
The name of the class imported. The class attribute is required.

The 'assert' item

Asserts that a specified class role is present at code generation.

<assert
    role = "..."
    />

The assert item has this single attribute:

role
The name of the class role being asserted. If no class is present (inherited or current) with this role, the code generation process aborts. The role attribute is required.

The 'method' item

Methods provide functionality for the component class. Methods can operate on specific objects, on the whole class of objects, or on other arbitrary data. Methods are inherited from parent classes unless the inherit attribute is set to "0". In the new and destroy methods, the class is addressed using the name "self".

<method
    name = "..."
  [ template = "..." ]
  [ abstract = "0 | 1"  ("0") ]
  [ condition = "..." ]
  [ inherit = "none | overlay"  ("overlay") ]
  [ export = "none | default | before | after"  ("default") ]
  [ private = "0 | 1"  ("0") ]
  [ cname = "..."  ("$(expand(method.name):c)") ]
  [ base = "..." ]
  [ return = "..." ]
  [ inline = "0 | 1"  ("0") ]
  [ trace = "..." ]
    >
    <option>
    <inherit>
    <argument>
    <animate>
    <dismiss>
    <declare>
    <local>
    <header>
    <footer>
    <doc>
    <invoke>
</method>

The method item can have these attributes:

condition
Specifies a condition which must be TRUE for the entity to be created. The condition attribute is optional.
template
If specified, defines an entity that acts as template for this entity. The template attribute is optional.
abstract
If set, the entity only exists in order to be inherited - no code is generated. The abstract attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 normal entity
1 abstract entity

name
The name of the method, used in the API. The name attribute is required.
inherit
Specifies whether this method may inherit from other classes. The inherit attribute is optional. Its default value is "overlay". It can take one of the following values:
Value Meaning
none may not inherit
overlay may inherit

export
Specifies how this item may be inherited. The export attribute is optional. Its default value is "default". It can take one of the following values:
Value Meaning
none may not be inherited
default inherited in the default manner
before inherited before existing items
after inherited after existing items

private
Specifies whether this method is for public or private (internal) access. The private attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 This method is for public use.
1 This method is for internal use only.

cname
The name of the generated C function; by default use the method name. The cname attribute is optional. Its default value is "$(expand(method.name):c)".
base
Generated functions usually begin with the class name. This can be over-ridden by the use of the attribute 'base'. The base attribute is optional.
return
The name of the returned value. This must be one of the items declared in the method body using 'declare'. The return attribute is optional.
inline
Specifies whether this method should be created as an inline function. The inline attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 Create a normal function.
1 Create an inline function.

trace
Specifies which trace object to use; default means global trace; 0 means no trace. The trace attribute is optional.

The 'argument' item

Defines one argument passed to the method. The body of this entity is used to document the argument.

<argument
    name = "..."
  [ condition = "..." ]
  [ type = "..." ]
  [ pass = "in | out | inout"  ("inout") ]
  [ index = "..." ]
  [ default = "..." ]
  [ precalc = "..." ]
  [ animate = "..."  ("1") ]
  [ ref = "0 | 1"  ("0") ]
  [ export = "none | default | before | after"  ("default") ]
    >
    <doc>
</argument>

The argument item can have these attributes:

condition
Specifies a condition which must be TRUE for the entity to be created. The condition attribute is optional.
name
The name of the argument. The name attribute is required.
type
The type of the argument, which is a native type name. The type attribute is optional. Its default value is "".
pass
Defines whether argument is to be passed into function, out of it, or both. "in" arguments are also to be considered const, i.e. not modified within the function. The pass attribute is optional. Its default value is "inout". It can take one of the following values:
Value Meaning
in Pass argument into the function
out Pass argument out of the function
inout Pass argument both into the function and out of it

index
The index if the argument is to be declared as an array. An empty string gives an indefinite array ('[]') The index attribute is optional.
default
The argument default value, used for integer arguments with value zero, and string and reference arguments with value null. The default attribute is optional.
precalc
The argument is precalculated by a macro in the header. The precalc attribute is optional.
animate
If set to 0, argument does not show in animation The animate attribute is optional. Its default value is "1".
ref
Pass argument by reference. If this option is selected, the name of the argument has a _p appended to it and a * appended to its type. A <declare> is also generated with the original name, type and default value of the argument. The ref attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 Pass argument by value
1 Pass argument by reference

export
Specifies how this item may be inherited. The export attribute is optional. Its default value is "default". It can take one of the following values:
Value Meaning
none may not be inherited
default inherited in the default manner
before inherited before existing items
after inherited after existing items

The 'animate' item

Provides a message or comment that will be shown when animating the component. The animate tag can be mixed with code. Defines an additional item (besides argument & declare) to animate.

<animate
    format = "..."
    name = "..."
  [ value = "..."  ("$(name)") ]
  [ condition = "..." ]
    />

The animate item can have these attributes:

condition
Specifies a condition which must be TRUE for the entity to be created. The condition attribute is optional.
name
The name of the variable to be output with the animation. The name to output for this value in the animation. The name attribute is required.
value
The value to animate. May be an expression. The value attribute is optional. Its default value is "$(name)".
format
The format string to output the variable. The format (eg '%s') for outputting the animated value. The format attribute is required.

The 'dismiss' item

Dismisses an argument: the argument is removed from the API and provided with a calculated value. Argument dismissal can be useful when building large classes with multiple parentage, where some arguments are redundant and can be removed from the API (thus making life simpler for the caller, which is a Good Thing).

<dismiss
    argument = "..."
    value = "..."
    />

The dismiss item can have these attributes:

argument
The name of the argument. The argument attribute is required.
value
The calculated value of the argument. The value attribute is required.

The 'declare' item

Defines data declarations for the code that follows. All local variables used in the code body must be placed in declare tags so that the final code can be correctly generated.

<declare
    type = "..."
    name = "..."
  [ condition = "..." ]
  [ default = "..." ]
  [ precond = "..." ]
  [ animate = "..."  ("0") ]
    />

The declare item can have these attributes:

condition
Specifies a condition which must be TRUE for the entity to be created. The condition attribute is optional.
name
The name of the variable. For non-atomic variables like arrays, this can contain the full variable declaration. Note that only atomic variables can be passed as arguments. The name attribute is required.
type
The type of the argument, which is a native type name. To use a reference to the the current class (a pointer in C), use the value "$(selftype) *". The type attribute is required.
default
The default value for the variable. To define a string value, you must enclose it in " symbols. The default attribute is optional.
precond
Allows code generated from 'declare' entity to be surrounded by a preprocessor condition, thus avoiding th annoying 'unused variable' compilation warning. The precond attribute is optional.
animate
If set to 1, value is animated at start of method as well as end. The animate attribute is optional. Its default value is "0".

The 'local' item

Variable definitions used by the method itself. The local definitions are a simpler way of defining blocks of variables than using declare items. Note you must use a declare for the return value.

<local>
    <doc>
</local>

The 'header' item

Defines a block of method code that should come before all bodies from the current and parent classes. Do not use for variable declarations, use 'local'.

<header
  [ export = "none | default | before | after"  ("default") ]
    />

The header item has this single attribute:

export
Specifies how this item may be inherited. The export attribute is optional. Its default value is "default". It can take one of the following values:
Value Meaning
none may not be inherited
default inherited in the default manner
before inherited before existing items
after inherited after existing items

The 'footer' item

Defines a block of method code that should come after all bodies from the current and parent classes.

<footer
  [ export = "none | default | before | after"  ("default") ]
    />

The footer item has this single attribute:

export
Specifies how this item may be inherited. The export attribute is optional. Its default value is "default". It can take one of the following values:
Value Meaning
none may not be inherited
default inherited in the default manner
before inherited before existing items
after inherited after existing items

The 'todo' item

Defines a change request, bug or other issue that needs changing in the iCL class. Todo items are formalised so that they can be extracted and processed mechanically.

<todo
  [ owner = "..." ]
    />

The todo item has this single attribute:

owner
The developer who registered the issue and will deal with it, specified as an email address. The owner attribute is optional.

Comments

Add a New Comment

Edit | Files | Tags | Source | Print

rating: 0+x

Author

iMatix Corporation <moc.xitami|ofni#moc.xitami|ofni>

Table of Contents

Installing and using OpenAMQ

Introduction to OpenAMQ: This document is an introduction to the concept of business messaging in general, and to OpenAMQ in particular. It is intended for new OpenAMQ users who wish to understand the problems that OpenAMQ solves, and how OpenAMQ can be useful in software applications.

Basic use of OpenAMQ: This document explains how to get OpenAMQ running on your system. It explains how to download the software, how to unpack and build it (if you are using a source package), and how to run basic tests on the resulting software.

Advanced use of OpenAMQ: This guide is for people who need to configure and manage OpenAMQ servers. We explain how to configure and tune an OpenAMQ server, covering these topics: logging, monitoring, high-availability failover, and joining OpenAMQ servers into wide-area federations.

Writing applications

Programming WireAPI: This is the main guide for developers who wish to use OpenAMQ in their applications. We describe WireAPI, the C/C++ API that OpenAMQ provides for accessing AMQP. Expert WireAPI users may wish to read the iMatix iCL guide, but this document is otherwise self-complete.

Programming PAL: This guide is for OpenAMQ developers who need a quick way to write test cases and simple scenarios. We explain the PAL language, an XML scripting tool that gives you a fast way to construct AMQP applications to test routing models, performance and stability tests, and other test cases.

Programming the Console: This document explains how to write applications that automate management of OpenAMQ servers via console automation. The OpenAMQ console automation architecture offers developers different ways of accessing the functionality of the console API and integrating it with their own preferred tools and management facilities.

Technical library

Developer's Guide to ASL: This is a technical guide for protocol developers who wish to use the iMatix ASL framework for the development of connected client-server protocols. ASL is a generic framework that uses a protocol modeling language to construct the whole infrastructure for a given protocol. ASL was built primarily to support AMQP.

Developer's Guide to iCL: This is a technical guide for developers who wish to understand how the iMatix iCL framework works. iCL is a class-oriented modelling language for C applications and is one of the basic frameworks used in iMatix applications such as OpenAMQ.

Developer's Guide to MOP: This is a technical guide for developers who wish to understand how the iMatix code generation frameworks are constructed. We explain the principles of model oriented programming, and the basics of code generation using the iMatix GSL language. This provides essential basic knowledge for anyone intending to modify the OpenAMQ software.

Developer's Guide to SMT: This is a technical guide for developers who wish to understand how the iMatix SMT framework works. To use this guide the reader should be familiar with the iMatix iCL framework, and the iMatix Model Oriented Programming principles.

RFCs

The CML Request for Comments: We describe a generic technique for managing AMQP servers from a remote client application. This technique consists of a standard transport mechanism built over AMQP, and a standard XML language used to exchange information between a management component built-in to the server, and a management application. This is a request for comments, it is not a standard.