Copyright © 1996-2005 by the Regents of the University of California.
genlib Reference Manual
Gregory S. Couch
gregc@cgl.ucsf.edu
Computer Graphics Laboratory
University of California
San Francisco, California 94143-0446
Draft version.
Last modified on 25 March 2005.
Table of Contents
- Description
- Class Relationships
- Command Line Use
- Statement Reference
- Container Class Description
- Collection Class Interface
- Generic Interfaces
- Toolkits
Genlib
assembles a set of C++ classes into a library as directed from the input file.
The input may incorporate prebuilt interfaces and implementations from system
and user toolkits.
It may also supply additional members functions and data for the classes.
The toolkit interfaces and implementations are written using a subset
of the genlib input file syntax.
Genlib creates a subdirectory to perform its library assembly.
After the library is built,
there is a separate include file for each class,
a library include file, and the library code archive.
The generated library include file includes all the class header files
and then lists all of the classes inline functions.
The inline functions are in the library include file because they may depend
on other class definitions.
There are also several scratch files left in the subdirectory by default,
a Makefile and one source and object code file for each class.
By fiat:
- all class names (of generated classes) start with a capital letter.
- class names don't differ from other class names by only an s nor
an underscore (_) in the last position.
A familiarity with the ISO C++ standard library is assumed in this document.
Genlib provides a simple scheme for storage management associated with
class relationships.
Collection Relationships
One strength of genlib is its ability to manage collection classes,
i.e., classes that contain instances of one or more other classes,
each class in a separate container.
The interface to the collected instances is generated
such that the particular container classes used
can be changed without any changes in dependent source code.
By default, containers contain pointers to the collected class instances.
This allows for instances to be collected in multiple collections
while letting containers rearrange their contents as needed.
If the pointer default is inappropriate for some reason,
the collected instance may be collected by value.
They also must have default constructors.
Note: there is a limitation of the ISO C++ standard library
where you must use the pointer default for maps.
Delete Relationships
A delete relationship between two classes means that
instances of the collected class will be deleted when
the collection class is deleted.
Such collected classes are restricted to be on the heap
(i.e., not local nor global variables, nor a data member of a class).
The C++ technique to restrict a class to the heap is to make its destructor
private.
If the deleted classes has no owner (see next section),
then a deleteMe member function is generated to access the destructor.
TODO: must delete relationships be asymmetric? Does genlib check for loops?
Owner Relationships
A collection class may be designated as the owner
of a collected class.
The owner class controls the creating and deleting of instances
of the collected class;
other collection classes can only add and remove pointers to instances
from their container.
An owner relationship implies a delete relationship.
Collected class may contain a "reverse" collection of the instances of the
collection class they are in.
If a reverse collection exists,
it is used to speed up deletions in the case
where a collection class is not the owner.
Example
An example collection class would be a Graph class
that contains instances of both Vertexes and Edges.
Vertex and Edge are also referred to as
collected classes.
Graph deletes and owns instances of both Vertex and Edge.
In addition Vertexes delete Edges.
Then by default, when an Edge is deleted,
the Graph searches all of its Vertexes that contain
the Edge
and removes it from those Vertexes.
If a reverse collection of Vertexes were present in an Edge,
then instead of searching the list of all Vertexes,
the Graph uses the reverse collection to find
which Vertexes to remove the Edge from.
Synopsis
genlib
[ -i ] [ -I ] [ -r ] [ -v ]
[ -c flags ] [ -C compiler ]
[ -n name ] [ -t directory ]
[ input-file ]
Options
- -i
- List known interfaces along with a one-line synopsis.
- -I
- List known implementations along with a one-line synopsis.
- -r
- Remove the files generated to build the library but not needed
for further compiling (e.g. the Makefile, the source files,
and the object files).
- -v
- Be verbosely liberal with warnings.
- -c flags
- Give additional flags to the compiler. The default flags are:
'
-I.. -I/usr/local/otf/include
'.
- -C compiler
- Set which compiler to use.
The default compiler is g++.
- -n name
- Change the name of the generated directory,
include file and library.
The default name is lib.
- -t directory
- Add a directory to the set of directories searched for toolkits.
The default directories are the current directory
and /usr/local/otf/resource/genlib/.
Example
genlib -c '-g -DFILENAME=\"xyzzy\"' input.gm
File Types
Genlib statements are restricted in the various files used as follows:
- Input File Format
- All statement types are allowed, except for provide interface.
- Implementation File Format
- All statement types are allowed, except for the rename statement.
- Interface File Format
- All statement types are allowed with the following exceptions:
code, destructor, implementation,
inlinecode, provide, and rename.
In addition the following statement types are modified:
constructor statements end with a semi-colon after the arguments.
There should be no data declarations (not enforced).
Only function declarations should be in members (not enforced).
Auxiliary files should only be header files (not enforced).
C++-style comments (starting with // until the end of the line) are allowed
anywhere.
Comments that appear inside a C++ section of a statement are copied through.
Statement types
- auxfile filename ;
- Add the filename to the set of files in the generated
subdirectory. If the filename has a suffix of .c, .cc,
.C, .cpp .cxx, or .m,
then it is added to the list of files to compile.
- code class {
additional code to implement interface
}
- Additional code need to implement class.
- constructor class [ ( arguments ) ] [ : member-initialization ] {
additional initialization code
}
- Give a constructor implementation.
Error checking that implementations match interfaces is done by the compiler.
- container name { options }
- Add a container class with the given name to the list of legal
container classes.
The various options are detailed below.
- destructor class [ virtual ] {
additional cleanup code
}
- Give a destructor implementation.
- implementation name [ in class ] ;
- Use a particular implementation (see Toolkits).
The in clause indicates that name is a class-parameterized
generic implementation.
- includefile filename [ in class ] ;
- Add the filename to the list of files that need to be
included for a class's interface. If the class is
missing then the filename is included in every class' interface.
- inlinecode class {
inline function declarations
}
- Add inline function declarations.
- members class [ : base-class-list ] {
additional member declarations
}
- Add declarations to a class' declaration.
By default, additions to interfaces are public,
and additions to implementations and input are private.
The base-class-list is an unparsed C++ list of classes to inherit
from.
- provide interface interface [ in class ] ;
- Indicates that this file provides
an implementation of the named interface.
The in clause indicates that interface is a
class-parameterized generic interface.
- provide container class [ owner expression ] in collection-class
[ byvalue ] [ keytype type
keymember expression [ keysfunc keysfunc ] ]
using container [ extra parameters ] ;
- Give the container class used to manage collections of
class instances in collection-class.
If the using clause is present,
genlib will generate an implementation
using the given container class.
container's must be from the ISO C++ standard library
or described in a container statement.
The optional parameters override the ones given
in the container class description.
If (and only if) the keytype clause is present,
then the container class must be a map-style class.
The expression should be a member function of class
that returns the map key of the right type.
The owner expression tells how the collection-class
reaches the owner of the class when it is not the owner.
If both the class and the collection-class
have the same owner class,
then instances are assumed to have the same owner
and the owner expression is ignored.
If the keysfunc clause is present, then the generated containers
function just returns the contained elements and a separate keysfunc
function is generated that returns the all of the keys. The map is still
available via the containerMap function.
- provide container class in collection-class
[ byvalue ] singular ;
- Indicate that instances of the collection-class
keep a reference to the class they are in.
genlib generates an implementation using a pointer.
- rename class to new-class ;
- Rename a class to a different class name, new-class.
This is so there can be multiple libraries using the same interface.
- require interface interface [ in class ] ;
- Indicate that the interface is a required part of the library.
The in clause indicates that interface is a
class-parameterized generic interface.
- require container [ delete ] class in [ owner ] collection-class
[ byvalue ] [ keytype type [ keysfunc keysfunc ] ] ;
- Indicate that the collection-class maintains a collection of
class instances.
If (and only if) the keytype clause is present,
then the container class used must be a map-style class.
If the delete is present,
then the default destructor behavior of collection-class
is to delete class instances that it contains when it is deleted.
If the owner keyword is given
then the collection-class instances control access
to class instances.
A consequence is that the class constructors will be private.
A class can only have one owner.
- require container class in
collection-class [ byvalue ] singular ;
- Indicate that instances of the collection-class
can access which instance of class they are in.
- typedef class { type }
- class is
typedef
'd as type.
This precludes the class from having any constructors,
destructors, or members.
All container classes are assumed to be compatible
with the ISO C++ standard library.
Container types are generated as name<instance-type>
or, for maps, name<key-type, instance-type>.
Some container classes require additional template parameters.
A default version of those parameters is given in the container class options
and can be overridden in the provide statement.
The options are:
- append string
- Tell how to append an instance to the container.
(Must have trailing semicolon.)
- extra string
- Provide default additional template parameters to container class.
- includefile filename
- Specify which include file contains container description.
Maybe be repeated.
- integerindex
- Indicates that the container can be indexed by integers,
like the ISO C++ vector class.
- keyslisttype
- keyslist
- map
- Indicates that container is a mapping,
like the ISO C++ std::map and std::multimap classes.
- remove string
- Tell how to remove an instance from the container.
(Must have trailing semicolon.)
- valueslisttype
- valueslist
If there is no includefile clause given,
then "name.h"
is included.
The argument string's given above can be parameterized with the
collected class type, FORCLASS; the collection class type, INCLASS;
the container class type, TYPE;
(if a map) the key type, KEYTYPE;
the actual container instance, CONTAINER;
the collected class instance, ELEMENT;
and (if map) the key expression, EXPRESSION.
Note that the various types above may or may not be pointer types,
depending on the delete relationships.
For example, the ISO C++ set class is described as follows:
container std::set {
includefile <set>
extra "less<FORCLASS>"
append "CONTAINER.insert(ELEMENT);"
remove "CONTAINER.erase(ELEMENT);"
}
Genlib knows about the following ISO C++ containers:
std::deque, std::list, std::map,
std::multimap, std::multiset, std::set,
and std::vector.
The descriptions for all of the standard ISO C++ container classes are kept
in the genlib resource directory, /usr/local/otf/resource/genlib,
in the containers.implementation file.
In addition, genlib knows about arrays as containers:
use a container name of otf::Array with an extra parameter that is
integral array size (e.g., for an array of two elements:
provide container ... using otf::Array extra 2;).
otf::Array is container class that adds ISO C++ iterators to C/C++ arrays.
As implemented, arrays have several limitations as containers
since they do not expand and contract as do the other ISO C++ containers.
They do not support the erase(iterator) member function
that removes a single element.
Consequently, an array cannot be used as a container
when a class must delete anther class.
Nor do they have an append clause nor a remove clause
in their description.
That means that arrays are not automatically updated like other containers are.
Despite these limitations, arrays can be very useful;
especially when all of the elements of the collection are given
in the class constructor.
Collection classes control all access to collected classes
through the following interface.
For each type of collected class, the class name, ClassName,
and the class name with the first character converted to lowercase,
className,
are used to generate several member functions.
In owner class
- ClassName *newClassName(....)
- Each generated private constructor has a corresponding new member function.
- void deleteClassName(ClassName *instance)
- Delete instance.
In non-owner class
- void addClassName(ClassName *instance)
- Add instance.
- void removeClassName(ClassName *instance)
- Remove instance.
In a singular collection
- ClassName *className() const
- Return pointer to collected class instance.
In a non-singular collection
- ClassName *findClassName(type key)
- Find an instance by its key.
For container classes that are not maps,
but can be indexed by non-negative integers,
the type is int.
Otherwise, this function is not generated.
- typedef container<args> ClassNames
- container type
- const ClassNames &classNames() const
- Returns the constant container instance.
In a map or indexable collection
-
ClassName *findClassName(keytype key) const
-
Find (first) element in collection with given key.
In addition, the following are generated for std::multiset and std::multimap:
- typedef std::pair<ClassNames::const_iterator, ClassNames::const_iterator> RangeClassNames
- Range type to simplify the use of the following find function.
- RangeClassNames findRangeClassNames(keytype key) const
- Return all of the element of the collection class that match the given key.
Example
Let Graph be the name of a collection class,
and g be an instance of that class.
Let Vertex be the name of a collected class
that is owned by Graph.
Then to iterate through all of the vertices, one would:
for (Graph::Vertexs::const_iterator i = g.vertexs().begin(); i != g.vertexs().end(); ++i) {
proccessVertexPointer(*i);
}
To provide the same interface in several classes,
there are class-parameterized generic forms
of the require, provide, and implementation statements.
In the interface and implementation files,
in every statement where a class name may be specified
(except for the rename statement),
the class name may be given as the word __CLASS__;
and __CLASS__ is replaced with the class
that contains the interface.
Similarly, in the members, constructor, destructor,
code and inlinecode statements,
the C++ text is scanned for occurrences of __CLASS__ to replace.
The system toolkits are kept in the genlib resource directory
/usr/local/otf/resource/genlib/.
Each toolkit is kept in a separate subdirectory
named toolkit.toolkit.
In that directory are kept the interface and implementation files,
respectively named interface.interface
and implementation.implementation.
An interface may have several implementations with different design trade-offs.
Toolkits may be nested.
By default, interface and implementation names
refer to ones in the current toolkit.
To refer to one in a different toolkit, use a hierarchical notation of
toolkit/name;
a subtoolkit would be referred to as
toolkit/subtoolkit/name.
The first line of the file should be a comment containing one-line synopsis
of the contents.
Other Issues
- How to integrate ISO C++ allocators.