Top   Types   Functions   Classes   Options   Index   Sources 

Quick Manual


Getting Started
The First Step
Instatiation
Your Makefile
Using the Data Structures
Data Structures
General
Status Codes
Zero Element
Translating Types to Identifiers
|Vector| of |Char|
Creation and Deletion
Appending
Inserting
Asking for the Number of Elements
Looking Up Entries
Appending Formatted Strings
Overwriting and Removing
Hash Tables of Integers
Hash Tables of Strings
Hash Tables of Strings: Symbol Tables 1
Hash Tables of Strings: Symbol Tables 2
Generating Symbols
(Almost) Statically Allocated Symbols
untemplatize
Project Structure
The |makeerwin| Script
Settings and User Data Types
Setting Options Via #define
Documentation Extraction
Erwin-CGen
Automatically Generated C-Interface
C++ Trivial Members
Wrappers Around Virtuals
Annotations
Member Function Annotations
Implementation Type of Groups of Members
Class Annotations
Global Annotations
C++ Extensions
Slots
Storage and User Interface
Visibility/Access Restriction
Namespace Class
Object classes
Sub-Classes
I want to ...
...use configure
...generate values in maps on the fly
...use symbols (=hashed strings)
...use fancy assertion macros (assert.pl)

Getting Started

When Erwin is used for applications or libraries, the data structures have to be instantiated. The library that was compiled and installed on your system contains templates only. These are converted into ANSI C/C++ implementations of data structures for the types you want.

Instantiating data structures generates C/C++ source code for a library that is linked to your program. This library is put into a sub-directory of your application that is usually called erwin. Generating this directory happens in several steps: one for generating the library skeleton and one step for each data structure that you want to use. The central program that does this is called untemplatize and will be described in all detail in one of the following sections.

The First Step

To begin, suppose you want to use a vector of char because you must handle strings and you want to prevent buffer overruns. Erwin implements printf-like mechanisms that are overrun-safe.

For using Erwin, its executables are needed. With $ERWIN being the installation path of the Erwin library on the system, the executables are located in $ERWIN/bin. You might want to add that directory to your shell $PATH. E.g. if Erwin was installed into /usr/local, the full path of untemplatize will be /usr/local/bin/untemplatize.

First, you need to generate the skeleton of the erwin directory.

untemplatize --init

There will be a erwin directory now that contains an empty library. It can be compiled with

cd erwin
./configure
make
cd ..

The library will appear as erwin/lib/liberwin.a.

To add data structures, invoke untemplatize for each of them. For instance:

untemplatize vector oType=char

The parameter vector specifies that you want a vector and the parameter oType=char specifies that the output type is char. The name of the data structure in C will be vector_char_t and in C++ VectorChar. All C functions manipulating a vector_char_t will have the prefix vector_char_.

Now, compile the new data type into the library:

cd erwin
make
cd ..

Instatiation

The preferred way to instatiate data structures is currently by using a small script makeerwin that contains a lot of calls to the Erwin tool untemplatize.

When starting a new project, you may want to generate an empty makeerwin script that you edit later to add data structures. To general an initial version of that script, use:

untemplatize -makeerwin

Included in this script, you'll find the inital invocation of untemplatize that initialises the erwin subdirectory where the library with its instantiated data structures will be compiled:

untemplatize -init \
             -include=settings.h \
             -xinclude=decls.h \
             -subdir=src,include,include \
             LESS_CPPFLAGS='-I../include -Iinclude -I..'

The call to untemplatize is encapsulated in the variable "$UNT", so prefer invoking that instead the explicit untemplatize in that script.

This is the minimal invocation. Some of the options are suggested now due to bad historic default values (especially -subdir). As said, the generated makeerwin already has this (but you can edit it if you want).

To start, you should create empty files settings.h and decls.h. You should now be able to compile the library already. Only -- it does not yet contain data structures.

To add data structures, add the following lines to the makeerwin script:

"$UNT" vector oType=char
"$UNT" vector oType='my_type_t *'
"$UNT" map    iType=int oType=int
"$UNT" list   oType='map_int_int_t *'

Put compile time options (#defines) in the settings.h file and type definitions needed as element types for your data structures, in the decls.h file (e.g. for my_type_t). The former will be seen by all Erwin files, including global headers, the latter will only be seen by data structure implementation. If you need prototypes of Erwin data structures (e.g. for your own data structures you want to use as keys or values in Erwin structures), then #include <erwin/forwards.h> in the decls.h file.

Because you put everything into one directory erwin, you can erase the whole subdirectory in your project directory:

rm -rf erwin

and regenerate everything with the makeerwin script:

./makeerwin

Your Makefile

To test the new library, you have to tell the compiler where the headers and library can be found. To do so, add the following CPPFLAGS to your invocation of the compiler. So edit your Makefile.am or Makefile.in or Makefile and add the

-I . -I erwin/include

Furthermore, the linker needs the path to the library (usually found in the LDFLAGS Makefile variable) and you want to link liberwin.a (usually by the LIBS Makefile variable).

So add the following to your LDFLAGS:

-L erwin/lib

and the following to your LIBS:

-lerwin -lm

Note that you need the math library, too (-lm). The flag -lerwin must be given before -lm.

When using GNU Autoconf, Erwin provides the macro ER_LIBERWIN to add these flags easily.

You should also include a dependency for your link rule on erwin/lib/liberwin.a to have it recompiled if something changes. The following rule achieves that (assuming that your target executable is called $(TARGET)):

.PHONY: dummy
dummy:
erwin/lib/liberwin.a: dummy
    cd erwin && $(MAKE)

$(TARGET): erwin/lib/liberwin.a

When using GNU Automake to preprocess your Makefiles, Erwin provides mechisms to add these rules easily.

Using the Data Structures

With the above Makefile settings, you can include the instantiated Erwin data structures by the following line in your source code:

#include <erwin/erwin.h>

Furthermore, you must invoke the function erwin_init. This initialises the Erwin library.

Then, you might want to test the vector of char your Erwin library should now provide. The C data type will be called vector_char_t. The C constructor is called vector_char_new and the C destructor is called vector_char_delete. Because the prefix depends on the types you instantiated the vector with, the reference manual will use the variable prefix Vector, so to get a precise description, look for Vector_new etc. in the reference manual.

The following miniature program outputs its parameters on standard output.

In C:

#include <stdio.h>
#include <erwin/erwin.h>

int main (int argc, char **argv)
{
    int i;
    vector_char_t *args;

    erwin_init (&argc, &argv);

    args= vector_char_new ();
    for (i=0; i < argc; i++) {
        if (i != 0)
            vector_char_append (args, ' ');
        vector_char_append_string (args, argv[i]);
    }

    printf ("Invocation: %s\n", vector_char_as_array (args));

    vector_char_delete (args);

    return 0;
}

To make the program a little nicer, your might want to output the arguments in such a way that they are suitable for passing them to the shell. You can use the format family for this. Simply replace the second _append command by:

In C:

vector_char_oformat (FO_QUOTE_SHELL, "%s", argv[i]);

In C++:

#include <stdio.h>
#include <erwin/erwin.h>

int main (int argc, char **argv)
{
    erwin_init (&argc, &argv);

    VectorChar args;
    for (int i=0; i < argc; i++) {
        if (i != 0)
            args.append (' ');
        args.format (FO_QUOTE_SHELL, "%s", argv[i]);
    }

    printf ("Invocation: %s\n", args.as_array ());

    return 0;
}

If it quite common to rename the vector types and especially the vector of char to v_char_t in C and VChar in C++. For this, untemplatize has the option --name which changes the 'stem' of the names. E.g.:

untemplatize vector oType=char --name=v_char

This changes vector_char_t to v_char_t, and all functions in C get the prefix v_char_. Because the C++ names are generated from the C names, you will get VChar for the C++ type.

Data Structures

This chapter will give an overview of typical data structures that Erwin can provide.

General

Status Codes

The Erwin data structures, currently vectors and maps, return a status code for almost every operation. This status code is stored in global variables. These are called vector_errno and map_errno.

There are three types of status code: 1) full success, 2) success wrt. to the definition of the semantics of the function, but still with a warning, and 3) no success. In all cases, the data structure will be left intact, so never will a destroyed data structure be the result of an operation.

Examples for full success are the success of an insertion of a new key into a map. If the key already exists, however, some functions do not enter it again but instead issue a warning. This is part of its semantics so the operation was successful, but issued a warning. An error would be the attempt to insert a value into a map when memory gets short. In this case, the value might not be able to be inserted. An error wil be indicated in the status code.

To check for fully successful operation, use VECTOR_IS_OK(vector_errno) or MAP_IS_OK(map_errno). To check for success with a warning, use VECTOR_IS_WARNING(vector_errno), etc, and to check for an error, use VECTOR_IS_ERROR(vector_errno) etc.

Note that if you use threads with your program, global variables for status codes will raise problems. Therefore, you can compile each instantiated Erwin library with thread support by globally defining ERWIN_THREAD_SAFE. Then, every structure has its own status code.

Zero Element

Every data structure has a zero element. By default, this is simply 0 cast to the output type. E.g. for int, it is |(int|0). For pointers, it is NULL. For double it defaults to 0.0.

The zero element is used in many situations, e.g. it is returned from a find operation on a map when no element was found.

For vectors, it is used to define what element terminates strings. E.g. when you convert a vector_char_t to char const * by using vector_as_array, this zero element will be used to terminate the string.

For each data structure, you can decide whether you want a constant zero element, i.e., one that is the same for all instances of that data structure, or whether you want a dynamic zero element that can be set individually for each instance of the data structure.

By default, dynamic zero elements are used in order to provide full programming freedom. If you want to improve the performance of your programs, you can recompile the instantiated Erwin library with some or all data structures using constant zero elements. E.g. for vectors of char, it is almost always suitable to use the following:

#define VECTOR_CHAR_CONSTANT_ZERO 1

Translating Types to Identifiers

Erwin codes the key and value type of the data structures in the identifier names of macros and functions. The translation is as follows.

letters

letters are converted to lower or upper case depending on the context inside the identifier: macros are upper case, functions are lower case.

spaces

spaces are converted to underbars

asterisk

the pointer type qualifier, the asterisk, is converted to the letter p or P, depending on the context (function vs. macro)

ampersand

the C++ reference type qualifier, the ampersand, is converted to the letter r or R. Its use as key or value is is strongly

deprecated*, hower!

Examples

The reference manual lists the function Map_new. With an instantiation of the map with a key type of char * and a value type of int, this becomes map_char_p_int_new.

The macro Map_CONSTANT_ZERO becomes MAP_CHAR_P_CONSTANT_ZERO for the same map.

Vector of Char

To begin with, the previous chapter already mentioned a useful data structure that can be used for handling strings: the vector of char. This data structure is designed to interoperate with the normal C strings char *. It provides functions to prevent buffer overruns which give raise to are very common security bugs.

Most of the following functions are available for all vectors, no matter which content type (oType) they have. The only exception will be the printf-like format function family.

To instantiate a vector of char, use the following command.

untemplatize vector oType=char

Vectors of char make the use of <string.h> almost completely superfluous if you wish.

Creation and Deletion

The default C language constructor for vector_char_t * is called vector_char_new.

The default C language destructor is called vector_char_delete.

Example

{
    vector_char_t *v= vector_char_new ();
    ...
    vector_char_delete (v)
}

In C++, the normal paradigms are followed, so the destructor is either invoked automatically by C++ or explicitly by operator delete, depending on whether you use pointer or not.

Example

{
    VectorChar s;
    ...
}

Or with manual memory management:

{
    VectorChar *v= new VectorChar;
    ...
    delete v;
}

Appending

Instead of using strcat with char * which often leads to overrun problems, you can safely append an arbitrary amount of charaters to vectors of char as long as your memory is large enough. The family of functions to use is Vector_append... and Vector_format....

There are several things you might want to append to a vector.

char

The function to use for single characters is called vector_char_append. Suppose you have a vector_char_t *v. The following code appends the letter A to it:

vector_char_append (v, 'A');
char const *

If you have a zero-terminated string to append, use the function vector_char_append_string:

vector_char_append_string (v, "Hello Europe!");
char const *, int

If you have an array of characters of length n, i.e., a string not terminated by zero but instead with a given length, use the function vector_char_append_raw. The following appends Hello:

vector_char_append_raw (v, "Hello Europe!", 5);
vector_char_t *

If you have another vector to append -- let's call it w, you can append it to v by using vector_char_append_vector:

vector_char_append_vector (v, w);

When using the C++ data structure VectorChar, the functions are shorter due to the possibility of overloading. Suppose s was of type VectorChar. Then the above examples in C would read as follows in C++:

s.append ('A');
s.append ("Hello Europe!")
s.append ("Hello Europe!", 5);
s.append (w);

Note that for clarity, the long function names are also available in C++, so the following is correct, too:

s.append_string("Hello Europe!");

Another important thing to know is that in C++, the above w may be of three different shades of the vector data type: vector_char_t const *, VectorChar const * and VectorChar const &. Obviously, the append function is multiply overloaded.

Rule of Argument Order: The number of elements is always given after the elements themselves.

Inserting

The insertion functions are basically named the same as the appending functions. They need another parameter, however, namely the insertion position. So the following functions insert at position i into the vector_char_t *v.

vector_char_insert (v, i, 'A');
vector_char_insert_string (v, i, "Hello World!");
vector_char_insert_raw (v, i, "Hello World!", 5);
vector_char_insert_vector (v, i, w);

Again, the C++ functions are named insert only. As with append, the long function names are available.

s.insert (i, 'A');
s.insert (i, "Hello World!");
s.insert (i, "Hello World!", 5);
s.insert (i, w);

So the interface to insert is analogous to that of append. Insert is only a more general function, because an insertion position is given.

Rule of Argument Order: The insertion position is always given before the elements.

Asking for the Number of Elements

The function vector_char_nentries returns the size of the vector in units of inserted elements. The reason for the name is to prevent confusion about the unit. E.g. the size might be meant to be the size in bytes. nentries, however, is unambiguous.

int n= vector_char_nentries(v);

Looking Up Entries

After having inserted entries into a vector, you will eventually want to look up some of them. The function vector_char_nth returns the _n_th entry. The elements are counted from 0 as usual in C/C++. The following assigns H to a.

char a;
vector_char_append (v, 'H');
a= vector_char_nth (v, vector_char_nentries(v) - 1);

There is an abbreviation for the first and the last element because they tend to occur often. The above code can be written as follows, too.

char a;
vector_char_append (v, 'H');
a= vector_char_last (v);

In C++, these functions are named accordingly:

char a= s.append('H').last();

Appending Formatted Strings

Some of the most powerful functions for character vectors are in the format family. Essentially they work like sprintf but have no buffer overrun problems.

Constructing a shell-quoted argument list was shown already in a previous section. This is very helpful when dealing with system or popen commands. Here is an example of how to read a bzip|ped file using |popen.

void process_file (char const *filename)
{
    vector_char_t *command= vector_char_new();
    FILE *f;

    vector_char_oformat (
        command,
        FO_QUOTE_SHELL,
        "bzip -dc %s", filename);
    f= popen (vector_as_array (command), "r");
    vector_char_delete (command);

    ... process f ...

    pclose (f);
}

In C++, you can generate temporary objects, print a formatted string into them and hand them over to a function accepting |char const *. Furthermore, you can #define the macro |VECTOR_CHAR_MANY_CASTS to have automatic casts from VectorChar to char const *. This eliminates some calls to as_array. The previous function can then be written as follows in C++:

void process_file (char const *filename)
{
    FILE *f= popen (VectorChar().format (FO_QUOTE_SHELL,
                        "bzip -dc %s", filename),
                    "r");

    ... process f ...

    pclose (f);
}

Overwriting and Removing

The functions for overwriting portions of vectors are called set for single elements and overwrite for longer sequences. The function names and argument order follow the same conventions as for append and insert.

For removing elements from vectors, the vector_char_erase function family can be used. It takes a vector, the index of the first element to delete and the number of elements to erase.

Hash Tables of Integers

There are cases when you do not know how large arrays will become. In this case, a hash table with key table int may be used. Suppose you want to store integers and map them to |double|s. Use the following command to instantiate such a hash table.

untemplatize map iType=int oType=double

iType=int specifies that the input type is int and oType=double specifies that the output type is double.

The most important functions for hash tables are: - insertion: There are two types of inserting into maps depending on whether overwriting is allowed or not. The functions are called map_int_double_insert of overwriting is prohibited and map_int_double_set if it is allowed. The following code shows the exit codes of insertion operations.

map_int_double_t *m= map_int_double_new ();
map_int_double_insert (m, 5, 5.3);
printf ("%s\n", map_strerror (map_errno));
map_int_double_set (m, 5, 6.7);
printf ("%s\n", map_strerror (map_errno));
map_int_double_insert (m, 5, 7.7);
printf ("%s\n", map_strerror (map_errno));

The first two commands will change the map for the key 5. The third one will not overwrite it so that 5 will still map to 6.7.

As usual, in C++ this looks as follows without showing the status codes.

MapIntDouble a;
a.insert (5, 5.3);
a.set (5, 6.7);
a.insert (5, 7.7);

- Finding values: To find values for a certain key, use the function map_int_double_find. The following code will print 6.7 after the above insertions:

 printf ("%g\n", map_int_double_find (m, 5))

In C++, simply use find:

printf ("%g\n", a.find (5));

If the entry is not found, this is indicated by the appropriate status code MAP_WARN_KEYNOTFOUND and by returning the map's zero element.

Hash Tables of Strings

The default hash value of maps is computed directly from iType. So if you have a map of char const *, the pointer will be hashed. This might be correct for symbols, but for strings, it is clearly not what you want. You want the value of the string to be used for the hash value and for key comparison, too.

To allow this treatment transparently, for each iType, a hash, a comparison, and an equality function can be given. The general comparison function is not needed for hash tables, but if you define it, the equality will be derived automatically, and additionally, you have an order on the keys which is usually handy to have. In the following, we will, therefore, define the comparison instead of the equality function for strings.

To tell the data structure how to hash and to compare |iType|s, two macros are used: iType_HASH and iType_CMP. The comparison function is defined by the macro iType_EQUAL but need not be given if iType_CMP is defined.

The hash, comparison and equal function must be able to handle the zero element correctly. In particular "`correctly"' means that crashing is not an option. For char const *, one might think about using strcmp as a comparison function but it will usually crash for NULL. Erwin provides the function string_cmp that does essentially the same as strcmp but handles NULL correctly. E.g. string_cmp (NULL, "") will return -1 (the empty string is larger than NULL).

Erwin also provides a hash function for char const *. It is called string_hash.

The best place to #define iType_CMP and iType_HASH is with the call to untemplatize.

untemplatize map iType='char const *' oType=int \

-DiType_HASH=string_hash \ -DiType_CMP=string_cmp

With these definitions, the following code prints 5.

MapCharConstPInt m;
m.insert (string_dup ("Hallo"), 5);
printf ("%d\n", m.find ("Hallo"));

The functions string_hash and string_cmp will distinguish strings by case, i.e. Hello is different from hello. Erwin has another pair of functions that are case-insensitive called string_case_hash and string_case_cmp. You can use these as well if you need that kind of hash tables. Of course, you can use your own functions. You need to provide prototypes, however. Read the section about the project structure for instructions how to do that in the best way.

Another function yet unmentioned is found in the above code: string_dup. It is the equivalent for strdup but can handle NULL correctly by returning NULL for it. It usually must be used with a hash table like the above when inserting keys, because the map does not know (yet) how to automatically copy its keys. So if you do not use string_dup, the key might be de-allocated somewhere in the code and vanish inside the hash table as well. This can lead to segmentation violations at worst and to failed assertions during rehash operations at best. This is the burden of C and C++: memory management is left to the user.

However, Erwin can handle this is some special cases that are most useful.

Hash Tables of Strings: Symbol Tables 1

The previous section showed problems about manually copying keys in Erwin data structures when pointers are used. To make life easier, all Erwin data structures can automatically copy keys and/or values when you provide the correct macro definitions.

Like hash and comparison functions, there are macros for copying and deleting keys and values that are called iType_ICOPY, iType_IFREE, oType_OCOPY and oType_OFREE.

The reason they do not end in _COPY and _FREE is that e.g. you might want to globally define all char * types to be copied as keys, but not as values. The distinction is then by the macro names CHAR_P_ICOPY and CHAR_P_OCOPY. To #define them, use the command line of untemplatize as before and add the appropriate definitions:

untemplatize map iType='char *' oType='int' \
              -DiType_HASH=string_hash \
              -DiType_CMP=string_cmp \
              -DiType_ICOPY=string_dup \
              -DiType_IFREE=string_free

Again, the functions string_dup and string_free are NULL-safe.

You might notice the difference of iType here when comparing the invocation of untemplatize to the previous one: it lacks a const qualifier. This is because Erwin will invoke string_free on that type and it cannot do that for char const *. The following section will remove that flaw and introduce the symbol tables data type in its final form.

Hash Tables of Strings: Symbol Tables 2

If you want to hide the memory management of pointer types totally from the user in order to minimize the possibility of accidentally doing something wrong, you can have Erwin use a different type internally from that visible to the user.

Symbol tables are a perfect example: you want to insert strings, i.e., char const *, without the need to worry about copying or freeing the keys, but Erwin must use char * internally to be able to invoke string_free. You can achieve that by defining the type iTypeVar additional to iType on the command line of untemplatize:

untemplatize map iType='char const *' oType='int' \
              iTypeVar='char *' \
              -DiType_HASH=string_hash \
              -DiType_CMP=string_cmp \
              -DiType_ICOPY=string_dup \
              -DiType_IFREE=string_free

This is a very good symbol table data type now. An abbreviation of the above call should be included in subsequent versions of Erwin however...

Actually, since this is a special map from char const * to int, it is wise to give it a special name by including another command line argument:

untemplatize map iType='char const *' oType='int' \
              iTypeVar='char *' \
              --name=symtab
              ...

Generating Symbols

Sometimes, you want to convert strings into symbols. The symbol table we have created so far can do that. One way of doing this is to insert a key with some value, and then use find_key to retrieve the pointer as inserted in the symbol table. That pointer will be equal for each two strings that is equal wrt. to iType_CMP, because the key is searched by that function and the internal key is returned, which is always the same one.

There is an abbreviation for this, however, called ensure. So to ensure that a key in inserted in the symbol table and to get the map's internal key, use the following code:

assert (symtab_ensure (m, "Hallo") ==
         symtab_ensure (m, string_dup ("Hallo")));

In C++, this is even shorter:

assert (a.ensure("Hallo") == a.ensure (string_dup ("Hallo")));

Note that ensure returns a char const *, not an int like find.

If ensure does not find the key in the map, it will be inserted with the zero element by default. You can change that behaviour by #defining the macro Map_ENSURE_VALUE(SELF,KEY).

(Almost) Statically Allocated Symbols

The script makesymbols.pl gathers C identifiers of the form sym_.... in the source code and produces a file that is a good include file for implementing a static table of symbols:

makesymbols.pl -output=symbol.inc -prefix=sym_ main.cc

This reads main.cc and writes symbol.inc. E.g. if there is a symbol sym_test, you'd get the following line in symbol.inc:

DEF_SYMBOL(sym_test,"test")

For each sym_... identifier, there is one such line. You can use this in a header file to declare those symbols:

#define DEF_SYMBOL(X,Y) extern symbol_t X;
#include <mylib/symbol.inc>
#undef DEF_SYMBOL

extern symtab_t symtab;
#define string2symbol(X) (symtab.ensure(X))

And then, in a C implementation, you can use this twice to implement the symbols:

#include <the_above.h>

#define DEF_SYMBOL(X,Y) symbol_t X;
#include "mylib/symbol.inc"
#undef DEF_SYMBOL

symtab_t symtab;

void symbol_init()
{
#define DEF_SYMBOL(X,Y) X= string2symbol(Y);
#include "mylib/symbol.inc"
#undef DEF_SYMBOL
}

No, invoke symbol_init() at program start and all the used sym_... constants are automatically hashed strings. With a bit of Makefile magic, you can make the regeneration process automatic.

To use symbols that have a non-trivial mapping from string value to symbol name, you can include the following comment somewhere in the files that makesymbol.pl scans:

symbol(sym_DOT, ".") Together with the above, this results in:

C:

DEF_SYMBOL(sym_test,"test")
DEF_SYMBOL(sym_DOT,".")

(If a usage of sym_DOT is actually found. The comment only declares the value, it does not generate a symbol entry in symbol.inc.)

untemplatize

Project Structure

The makeerwin Script

Put all calls to untemplatize into a script in the parent directory of the erwin directory. This makes re-creation of that directory most easy. A skeleton for that file comes with the Erwin library. This searches for the untemplatize program in the most correct way. With $ERWIN being the installation directory of Erwin, it is located in FIXME.

Settings and User Data Types

Erwin libraries cannot always be compiled stand-alone, because you need to provide user defined data type definitions as elements or keys of data structures or because you want to change the behaviour be #defining certain settings.

There are several methods of providing Erwin with the necessary definitions. The advised one is to use two addtional files: settings.h and decls.h that you put in the directory erwin is a sub-directory of.

settings.h will be included before any of the Erwin definitions are made and decls.h are included after the global Erwin definitions are made but before any data structure definition.

With this structure, global definitions like the need for thread safeness must be #defined in settings.h whereas data structure definitions of user types should be given in decls.h.

To tell Erwin that these files exist, use the following --init command:

untemplatize --init --include=settings.h --xinclude=decls.h

See the help text of untemplatize for details (untemplatize --help).

Also be advised to use pointers to user defined data structures only. Although it is no problem at all to use aggregate data in Erwin data structures, the #inclusion order becomes important and is error-prone. Usually, you end up with Erwin data structures you need in your data type, which you want to use in Erwin data structures in turn. Then you cannot use the simple #include <erwin/erwin.h> but need to split it into several includes for each data structure. This is possible and Erwin is clearly designed to do this, but it is not too easy.

If you have a data structure called my_data_t that you defined to be a struct, use the following definition in decls.h.

struct my_data_t;

Usually, you will not want to put the whole definition here, but into some of your other header files that will contain more definitions not needed by Erwin. To use a vector of pointers of my_data_t, issue the following command.

untemplatize vector oType='my_data_t *'

With the above --init --xinclude=decls.h, Erwin will certainly find the forward declaration of the data type and can handle pointers to it.

Setting Options Via #define

Many properties of instantiated data structures can be changed at compile time by preprocessor #defines. Usually, the settings can be made for a single data structure, e.g., vector of char, or for all vectors or for the whole library.

The names of the macros for a family of data structures is simply the special name without the coded type. E.g. to set constant zero elements for vectors of char, #define the macro VECTOR_CHAR_CONSTANT_ZERO. To set it for the whole data structure, use VECTOR_CONSTANT_ZERO.

In order to set a property for the whole library, replace the family name by ERWIN, e.g. ERWIN_CONSTANT_ZERO.

Note that this generalisation is not yet fully implemented for all macro, but at least most boolean macros work like this.

There are settings that are per element type, too. E.g. oType_CMP. For char * this will become CHAR_P_CMP.

Very few macros even have forms for elements type, for families of data structure, and generalised forms. E.g. there also is oType_CONSTANT_ZERO which is the default for Vector_CONSTANT_ZERO if that is not defined.

The names used for changing settings will always be given in the most fine-grained form in this manual. E.g. the name Vector_CONSTANT_ZERO will be used instead of the other above macros names meaning it can be set individually for each vector type.

There are some settings that can only be made for the whole library and not for single data structures, like ERWIN_REQUIRE_DETERMINISM.

Documentation Extraction

Erwin has its own tool to extract documentation from libraries. It was initially developed because the C++ dialect written by me was unfortunately not understood by existing documentation tools (I had macros to declare properties/slots in C++, and member definition by macros was not understood by the tools). This has changed now -- tools exist where special plug-ins may be written. However, the Erwin tool erwin-cgen has become such a powerful tool that there is currently no urgent need for another documentation extraction tool. Instead erwin-cgen has some advantages over other tools.

erwin-cgen produces HTML documentation and/or a browsable GDL class hierarchy file with a lot of documentation encapsulated in node information for easy browsing.

erwin-cgen parses all comments. It was initially developed for libraries that have long lived without any documentation extraction, so the goal was to extract the interesting information from existing comments that are not specially marked as extractable documentation. This has become a design goal now: to extract docu from files that read well and natually in the source code version, too, without (much) special markup or hot comments.

erwin-cgen includes a text2html converter, so that the source code comments can be written in a native, ASCII form readable in source code view, while the extracted (HTML) documentation contains native markup. List, tables, and source views are supported.

Invoke

erwin-cgen --help

for the options this tools has.

Note

erwin-cgen is relatively young and still in flux, so some minor incompatible changes are likely in future releases.

Erwin-CGen

Automatically Generated C-Interface

The primary goal of Erwin-CGen was to automatically create a C interface to a C++ library. The author felt that such an interface must be trivial: prepend the class name to each function, resolve the overloading by function suffixes, make the pointer types available and, there you are, you have a C interface.

The whole story is a bit more complicated, but in principle, this is how it works.

erwin-cgen assume some kind of basic structure a C++ library has:

Example

struct MyClass {
  ...
     void something (int &result, double y) const;
};

erwin-cgen can generate a C wrapper with the following header file:

#ifndef __cplusplus
typedef struct MyClass MyClass
#endif

#define my_class_t MyClass

extern void my_class_something (
    int * result, my_class_t const * self, double y);

Invoke

erwin-cgen --help

for the options this tools has. Sets of options can be stored in config files by using @configfile to include the file.

erwin-cgen is the most recent tool of Erwin and still under heavy development. However, it is already used in commercial products, which require interface stability, so incompatible changes are highly improbable even if extensions are included in the future. OTOH, the produced documentation may look different with every release and the C interface may become more sophisticated.

The current languages and dialects that erwin-cgen supports are C, C++ without templates, Erwin C/C++template classes, and Erwin C/C++ library headers. There is no modular input file format handling yet.

C++ Trivial Members

erwin-cgen also provides functionality to automatically generate trivial C++ members functions.

Wrappers Around Virtuals

I wanted to have NULL-safe virtual functions that just return 0/NULL/false/whatever-default-value-you-want in case the this pointer is NULL. Normal virtual functions crash (SIGSEGV) for a NULL pointer. To achieve my goal, I always implemented two functions: the virtual one, which was made protected, and a wrapper, checking for NULL and otherwise invoking the virtual function, which was public. erwin-cgen can create this wrapper for C++:

public:
   //! implementation: redirect
   int something() const;
protected:
   int v_something() const;

The function something is automatically implemented to check for NULL, return 0 (the default value of 'int') in that case, and otherwise invoke v_something. For a different name of the virtual function, use:

public:
   //! implementation: redirect, redirection: v_something_int
   int something(int) const;
   //! implementation: redirect, redirection: v_something_double
   int something(double) const;
protected:
   int v_something_int(int) const;
   int v_something_int(double) const;

As you can see, this is awesome for overloaded functions that allow individual overloaded in the virtual case.

Furthermore, many virtual functions are trivial: they are meant to be overloaded, so the base class implementation returns a trivial value. erwin-cgen can create trivial implementations, too:

protected:
    //! implementation: trivial, trivial-value: -1
    int v_something_int(int) const;

FIXME: The properties/slots implementations can also be generated automatically, but this is currently quite undocumented. erwin-cgen parses special macros for declaration of properties/slots for this and implements the access functions for them.

Annotations

The following is a list of annotations that erwin-cgen recognises. As seen above, they are usually prefixed to member functions in a special 'hot' comment, introduced with ! above. You can change the hot character with the --hot-char option.

Member Function Annotations

These annotations are prefixed to a member function in a 'hot' comment, e.g.:

//! trivial-result: -1
virtual int blah() const;

These can also be prefixed to slot declarations (see "C++ Extensions").

implementation: trivialautomatic implementation: this function always return a trivial/constant value. the value is usually derived from the result type, but can be also be set with the trivial-result annotation.
implementation: redirectautomatic implementation: this function is just a trampoline for calling another one. By default, v_ is prepended or deleted from the beginning for finding the name of the function to call, but you can also set the function manually with the redirection annotation.
implementation: inherita bit like redirect, but the function simply calls the same member in the super class.
implementation: manualeven in a block of automatically generated functions, this one is implemented manually.
null-ok: BOOLin the automatic implementation of this member, this == NULL is ok and results in immediate return (possibly with a given trivial-result).
check-sig: BOOLwhether macros for checking the signature of the class pointer are invoked for this method (that macros have to be implemented by the user).
trivial-result: VALUEsets the trivial result of this function for both null-ok and implementation:trivial.
redirection: NAMEoverrides the default algorithm for finding the name of a member to invoke for the implementation:redirect annotation.
c-name: NAMEmanually set the name this function has in the C interface in case the C++ name is not unique due to overloading. Note that functions whole only difference is a const will be automatically take a _const suffix in C for one variant.
c-suffix: NAMEinstead of specifying the full C name as c-name, this sets a suffix to append to the C++ name to get the C name. An underbar is automatically inserted if missing.
inherit: BOOLwhether the functions is inherited into sub-classes by erwin-cgen.
c-inherit: BOOLthis function is not inherited in the generated C interface.
doc-inherit: BOOLthis function is listed in sections of inherited members in the documentation.
doc-link: NAMEinsert an automatic link of the given name to this function. Text that matches this identifier will link to this member.

Implementation Type of Groups of Members

Instead of prefixing implementation:blah for each member in a group of several members, you can use the following syntax:

trivial protected:
    virtual char const * v_blup() const;
    virtual int          v_blah() const;

This is most useful in a typical Object class that declares a lot of functions to be overloaded, and provides trivial implementations for them. Often, you want non-virtual wrappers to allow this==NULL. To implement that, use:

redirect public:
    char const * blup() const;
    int          blah() const;
trivial protected:
    virtual char const * v_blup() const;
    virtual int          v_blah() const;

This results in blup returning NULL if this==NULL and invoking v_blup otherwise and in v_blup returning NULL, too. v_blup can now be overloaded. Similar for blah.

Class Annotations

These go anywhere inside a class definition.

abstractmanually tell erwin-cgen that this is an abstract class. It cannot tell this purely by inheritence yet, so you sometimes need to tell it. If you use to abstract macro from classdef.h or if you define a pure virtual function before the first constructor declaration, then you don't need this annotation.
c-class-ignoreignore this class in the C interface. This disables any reference to that class, so members that use this class are also ignored in the C interface.
class-ignore(almost) completely ignore this class
set-c-suffix: NAMEset a suffix to be appended to all following C names of member functions. Note that an underbar is automatically inserted if missing.
reset-c-suffixreset that suffix
doc-link: NAMEmake NAME link to this class in the entiret documentation.
map-class-name: NAMEfor the last local_class encountered, declare the C name of that class in case the default method of translating upper case characters to lower case + underbar in not desired. This can only be used in the namespace class.
map-func-name: N1->N2for the C++ member N1, sets the C name N2 globally. It can be overridden locally with the c-name annotation. This can only be used in the namespace class.

Global Annotations

These are read everywhere in the input files.

section: HEADLINEadd a new section with the given headline to the documentation. The rest of the comment is the text of the section. You can specify an ordering number by prefixing the HEADLINE with a number and a colon: headlines are then ordered by that number instead of in the way they were read. This annotation must be given in a C comment, not a C++ comment.
enum: REGEXP, REGEXP,...defines an enum given with #define macros. All the macros that match one of the given patterns are considered part of the enum.

C++ Extensions

For programming with slots (similar to what is called 'properties' in other projects and languages), Erwin provides macros for generating access functions. This is a bit ugly, but it has advantages:

To use this, use the following code in C++:

#include <xyz/erwin/classdef.h>

Here, xyz is the library prefix.

At the end of your file, use:

#include <xyz/erwin/classundef.h>

This cleans up the macro namespace a bit.

erwin-cgen is aware of all the macros and correctly generates documentation.

Slots

Consider the following class declaration:

struct XyzTest: public XyzObject {
public:
    int id;
...
};

Don't use 'class', but 'struct' if you want to make it possible to generate a C interface.

If you want to have a proper slot, you can write:

struct XyzTest: public XyzObject {
public:
    child_rw (int, id, "The unique id of this test")
...
};

child_rw means that the given slot has read-write access and is a direct 'child' of the class. Non-pointer types are always children; the alternative for a pointer would be to be a cross reference (macro xref_rw) to another structure. For pointers, being a child means this class is responsible for memory management, while 'xref_rw' means the class is not responsible for that, but instead another class is. In many environments, this is no difference (e.g. with garbage collection or reference counting), so you could just stick to using 'child_...' macros everywhere.

All the macros generate at least the following declarations: C++:

 private:    int          _m_id;
 protected:  int        &  m_id ();
             int  const &  m_id () const;

_m_id is the private data declaration, that should only be used during object initialisation. The other two protected accessor functions can be used after that.

The implementation of the accessor functions does run-time checks for object validity, so using them improves robustness: e.g. this == NULL will be detected early. For the _obj macros discussed later, it also checks the stored value, therefore, you cannot used them during object initialisation. (You can customise the checks to include object signature checks, etc.)

Note

By default, the implementation of the slots is generated by erwin-cgen, so you must use that script for using the slots, too (--gen-members). If you want inline definitions, use the following definition in settings.h:

#define XYZ_ERWIN_CLASS_USE_CGEN 0

Note that erwin-cgen can generate more sophisticated implementations due to knowing default result values. E.g., you can use a trivial-value annotation to change it and you can allow slots to return that value if this == NULL, which the inline macros cannot do. The option --slot-trivial enables extended slot generation support with erwin-cgen.

Storage and User Interface

The above 'child_rw' macro adds the following declarations for read-write public access:

public: int id() const;
        void set_id(int);

If you want the user to get access via a pointer, but internally, you want to keep the data as an inline data, use child_rw_ref. The following functions for public will be created instead:

public: int const *id() const;
        int *id();           // Note: write access via reference here!
        set_id (int const *);

If the data should be stored as a pointer instead, use child_rw_ptr. This is similar to using 'child_rw' with a pointer argument, only the macro provides a 'reset' method additional to the set() method for setting the data pointer to NULL. Also, different access modes are available.

Finally, if you want to have a pointer to a class that also uses the classdef.h structure, use child_rw_obj. That macro is similar to child_rw_ptr, but enables additional run-time checks for debug versions, and includes automatic reference counting if you enable that (by defining the macros XYZ_REF and XYZ_UNREF appropriately.)

Summarizing, the following data kinds are available:

MacroStorage in StructUser Interface
<td>no suffixnon-pointer data membernormal access w read and write accessors
..._refnon-pointer data memberaccess via pointer
..._ptrpointer to dataaccess via pointer + reset() method
..._objpointer to other classaccess via pointer + reset() + automatic refcounting

Visibility/Access Restriction

Above, we always used read-write access to the member data. Of course, there is also read-only and private access. The following access types exist:

..._rwread-write access as shown above.
..._roread-only access: no modification of the data member is allowed in the public interface. The private and protected parts of the interface are the same. You can provide manual implementations of the missing slot accessors, of course.
..._privno public access at all. Again, you can add the accessors manually.
..._riread-only/write-inheriting access: for a const-pointer to the class, no write access is allowed. But if the this pointer is non-const, you can access the slot. This protection mode is only available for _ptr and _obj slots.

Note

you can easily switch between child_rw_ref and child_ri_ptr and vice versa, since the user interface and access protections are quite the same; only the reset member is missing for child_rw_ref.

Namespace Class

Sometimes it is desirable to have a namespace class that is not implemented as a namespace, e.g., because you are conservative wrt. portability. (Currently, no support for real namespaces is provided. This will probably change in future versions of the erwin-cgen program.)

The file xyz/erwin/classdef.h provides macros to let you declare local names in the namespace class for use within the class hierarchy that lack the library prefix you are using for the global definitions. E.g., consider the following declaration:

typedef char const *XyzSymbol;

Then, assume you have a 'namespace' class:

struct Xyz {
    typedef XyzSymbol Symbol;
    ...
};

And you might want to provide a convenience name for C:

#define xyz_symbol_t XyzSymbol

And to use this in C++ as well, you might also include this in the namespace class definition:

struct Xyz {
    typedef XyzSymbol    Symbol;
    typedef xyz_symbol_t symbol_t;
    ...
};

This becomes a lot of stuff to remember to implement for each type you define.

erwin-cgen can automatically generate the #defines for C and also the typedefs for C++. This section shows how.

Firstly, the macro local_type declares the above stuff in C++, so you need only write:

typedef char const *XyzSymbol;
...
struct Xyz {
    local_type (symbol, Symbol)
    ...
};

There are also local_enum and local_union, which are special to erwin_cgen to generate the correct C interface declarations, but are actually only aliases for local_type in C++.

Secondly, erwin-cgen can generated all the local_... stuff automatically as well when properly instructed (with --auto-announce, --write-forwards, --write-local-class), so the only thing you have to write to get all the above declarations automatically is:

typedef char const *XyzSymbol;

#include <xyz/gen-forwards.h>

...
struct Xyz {
#   include <xyz/gen-local.h>
    ...
};

The nice thing is that you only need two lines for all your declarations when using classdef.h together with erwin-cgen.

Object classes

Often a class hierarchy contains a top-level 'Object' class that contains printing, comparison, etc. as pure virtual functions to be implemented by sub-classes.

I often include dynamic casting operators for every sub-class in the Object class: these are simple virtual functions that return NULL if the class does not have the desired type, and which return this if it does.

The function are called just like the class with a 'as_' prepended.

The advantage over C++ RTTI's dynamic_cast is that the as_ virtual casts work in O(1). The disadvantage is that this does not work for user extensions, since all sub-classes must be known in advance. However, you can still use RTTI's dynamic_cast if you want -- there is no contradiction. Another disadvantage of manual implementations is the additional work the programmer has.

The classdef.h header file reduces this work. And erwin-cgen further reduces the effort needed greatly.

Imagine you have:

struct XyzObject : public Xyz {
    virtual XyzTest *as_Test() { return NULL; }
    virtual XyzBlah *as_Blah() { return NULL; }
    ...
};

This is nice, but has two problems: a) const pointers are missing, b) the casting crashes for this == NULL. A full implementation would be:

struct XyzObject : public Xyz {
protected:
    virtual XyzTest *v_as_Test() { return NULL; }
    virtual XyzBlah *v_as_Blah() { return NULL; }
public:
    XyzTest *as_Test() { return this == NULL ? NULL : v_as_Test (); }
    XyzTest *as_Blah() { return this == NULL ? NULL : v_as_Blah (); }
    XyzTest const *as_Test() const { ...cast and use above... }
    XyzTest const *as_Blah() const { ...cast and use above... }
    ...
};

This is shorter with classdef.h's 'announce_class' macro:

struct XyzObject : public Xyz {
    announce_class (Test)
    announce_class (Blah)
    ...
};

Note

currently you need erwin-cgen to generate the function bodies of the members declared in the macros.

Additional to the 'as_' functions, you get 'cast_' functions that never return NULL, but will generate a run-time error in debug versions of your library, and which will by pre C-style static casts in shipped versions. There are for casts that you (think you) know to work.

To further reduce type work, erwin-cgen can generate the announce_class macros automatically (--announce-class, --write-announce), so you just need:

struct XyzObject : public Xyz {
    general_object
#   include <xyz/gen-announce.h>
    ...
};

The macro general_object declares a few functions needed for subclasses in the following section (e.g. klass_name, klass_id, etc.).

Sub-Classes

For the dynamic casting to be implemented in sub-classes, you need to do overload the v_as_... functions. This can be done by a macro from classdef.h. For the macro to work, you must slightly modify your class declaration style:

Normal style in C++:

struct XyzTest: public XyzObject {
  ...
};

Required style for classdef.h:

#define THIS  Test
#define SUPER Object
struct XYZ_THIS: public XYZ_SUPER {
    abstract  // or 'concrete' for concrete classes
    ...
};
#undef SUPER
#undef THIS

This is a bit ugly, but you get nice benefits: first of all, it's easy to change the class name without changing multiple places: you can declare constructors and destructors by using XYZ_THIS as a class name.

Wrt. the classdef.h, you get the following benefits: the abstract and concrete macros implement the v_as_... macros (for this, they use the definition of THIS), further, concrete generates a function v_klass_name(), which returns the class name as a string, and it generates a function v_klass_id that returns a unique integer identifier for the class. This requires erwin-cgen to generate an enum (--write-ids) and you must include this enum. If you do not want IDs, define the following in the settings.h file:

#define XYZ_ERWIN_CLASS_ID 0

And finally, the macros implement two typedefs This and Super to be type aliases of the current, and the super class (multiple inheritance is currently not supported). In total, you get:

struct ... {
protected:
    virtual Test *v_as_Test() { return this; }
    virtual char const *v_klass_name() const { return "Test"; }
    virtual int v_klass_id() const { return xyz_id_Test; }
public:
    typedef Test   This;
    typedef Object Super;
};

By default you will get reference counting member that wrap around a reference counting functions in Object*. If you don't want refcounting, define the following in the settings.h file:

#define XYZ_ERWIN_CLASS_REFCOUNT 0

Again, for all the member implementations, you need erwin-cgen, unless you #define XYZ_ERWIN_CLASS_ID 0.

As said, multiple inheritance is currently neither supported by erwin-cgen nor by classdef.h. I regard it as bad style, but of course, other people may have different opinions.

I want to ...

...use configure

Before ./configure, the erwin subdirectory must have been created. So for developpers, the normal process of autoconf && autoheader && ./configure && make is extended by the ./makeerwin step at the beginning:

./makeerwin
autoheader
autoconf
./configure
make

Actually, due to many checks that Erwin's configure performs, you are likely not to need autoheader in every small project (unless, of course, you have configuration options to pass to C).

In configure.in, you can use:

builtin(include, erwin/acmake.m4)

...

ER_CONFIG_ERWIN

This invoke erwin sub-directory configure. All the rest is the same as with a Make procedure without configure.

There are some interesting improved versions of AC_ functions:

ER_PREFIX_PROGRAM
ER_PROG_CC
ER_PROG_CXX

And some other nice stuff:

ER_IF_GCC(then, else)
ER_IF_GXX(then, else)
ER_BROKEN_CTYPE        dnl #define/#undef BROKEN_CTYPE
ER_WORKING_MUTABLE     dnl #define/#undef WORKING_MUTABLE
ER_WORKING_EXPLICIT    dnl #define/#undef WORKING_EXPLICIT
ER_PROG_GNU_M4         dnl @M4@
ER_PROG_GNU_MAKE       dnl @MAKE@, @GNUMAKE@
ER_PROG_LATEX          dnl @LAMBDA@, @OMEGA@, @LATEX@, @DVIPS@
ER_PROG_LORDER_TSORT   dnl @LORDER@, @TSORT@

ER_CONDITION(prefix, shell_test)

The latter, depending on the valure of the shell_test defines two variables prefix_TRUE and prefix_FALSE that can be used in the Makefile to enable or disable options:

configure:

ER_CONDITION(ATHOME, test x"`uname -n`" = xmymachine)

Makefile:

CPPFLAGS = @CPPFLAGS@ @ATHOME_TRUE@ -DATHOME

...generate values in maps on the fly

Use Map_find_ensure() instead of Map_find(). For it to work, you'll need another option to untemplatize. Let's assuming you want a map from int to int and another map from int to that previous map. This implements a map from int to int to int. We'll store a pointer to the sub-map, not the map itself, so we have the following basic lines in makeerwin:

untemplatize map iType=int oType=int
untemplatize map iType=int oType='map_int_int_t *' \
      name=map_int_int_int

We renamed the second map, since the default 'map_int_map_int_int_t_p' does not look nice.

To have the second map perform the allocation and deallocation of its value type transparently use the following instead:

untemplatize map iType=int oType='map_int_int_t *' \
      name=map_int_int_int                         \
      -DoType_ENSURE_VALUE"(X)=map_int_int_new()"

If you want the rest of the allocation to be automatic as well, add the following lines:

      -DoType_OCOPY'(X,Y)=map_int_int_copy_err(X,Y)' \
      -DoType_OFREE='map_int_int_delete'

Now you can write:

MapIntIntInt a;

a.find_ensure(5)->set(6, 7)   // introduces a map for value 5
                              // on the fly if it does not exist.

a.erase (5);                  // erases the whole sub-map, too

...use symbols (=hashed strings)

See above in section "Hash Tables of Strings: Symbol Tables 2".

...use fancy assertion macros (assert.pl)

You might know about GTKs g_return_if_fail and g_return_val_if_fail. I liked those better than assert, but wanted a more flexible set of macros.

I found that the potential set is actually infinite, so a Perl script was written to generate the macros into a file usually called failure.h. Invocation is easy:

assert.pl main.cpp tools.cpp whatever.cpp -o failure.h

The macros have the following form:

(prefix_)(scope_)command_{"if"|"unless"}something({"p"}actions)?

The prefix is your library prefix set with the option --prefix=.... It is used to ensure that there are no name conflicts with other libraries if you export the failure.h file.

The scope is for defining whether it is a normal assertion failure (then this is empty) or whether the check that should be there even with -DNDEBUG, in which case this reads "error_"

The "p" stuff is for "print": you can provide expressions that are printed in case of an assertion failure. This is nice for diagnostics. The actions are mainly format specifiers the way you know them from printf, and some additional stuff.

Examples

mylib_return_if_fail(X)
mylib_break_unless_null_pii(X, i, o)
mylib_nothing_if_out_of_bounds (i, 50)
mylib_error_exit2_if_fail_pxx (a < b, a, b)

The following commands exist:

return, break, continuethe corresponding C statement
return_vallike return, but with a value
return0, return1, ...abbreviation for integer returns. m =
return_m1_...'m' = 'minus' ...
nothingprint a message, but do nothing else
exituse abort() to terminate
exit0, exit1, ...use exit(value)
true, falseexpression evaluating to 1 or 0
nullsame as 'false'

The following checks exist:

failevaluate to false triggers failure
nullcheck for null
null2, null3several null checks in one macro
null2i, null3iincremental: (a != NULL) && (a->b != NULL)
reachedalways triggers failure
out_of_boundschecks 0 <= test < max
crashchecks for SIGSEGV or similar

out_of_bounds also prints the corresponding values.

return_if_crash is a statement and cannot be used in expressions. It also needs quite a lot of stuff for an implementation and is not guaranteed to work on all platforms.

The following additional actions exist:

d, i, u, o, x, Xprints an integer in the given format
pprints a pointer
bprints a boolean
cprints a character
f, g, e, Eprints a floating point number
sprints a string
tsame as 's' but does not echo the expr.
Oprints an object: (X)->print_err(stderr)
2, 3, 4, ...repeat the previous action
Cevaluates an expr. (or executes a statement)

Another example:

error_return17_if_null2i_pi2C (a, b, i1, i2, fclose(f))

If case a == NULL or a->b == NULL, this fails, in which case first i1 and i2 are printed as integers and then fclose(f) is executed. After that, the normal failure action is executed, namely return 17. This macro is not disabled by -DNDEBUG.

For Emacs syntax highlighting, the following regexp matches a failure macro (and a few other failure macros, too):

"\\b\\(pagassertx?\\|assert\\|[a-zA-Z0-9_]*\
\\(\\(g_\\|error_\\|err_\\|warn_\\|warning_\\)?\
\\(true\\|false\\|null\\|return\\(_m\\)?[0-9]*\
\\|return_val\\|exit[0-9]*\\|break\\|continue\\|nothing\\|false\\|true\\)\
_\\(if\\|unless\\)\
_\\(null[0-9]*i?\\|crash\\|fail\\|reached\\|out_of_bounds\\)\
\\(_p[a-zOCEX0-9]*\\)?\\)\\)\\b"

If failure.h is included after liberror.h, then the assertion macros print via the Message Library 'liberror'.

If failure.h is used in the Linux kernel, the assertion macros print via printk.

Index

Stoppt die Vorratsdatenspeicherung
November 26th, 2007
Comments? Suggestions? Corrections? You can drop me a line.
zpentrabvagiktu@theiling.de
Schwerpunktpraxis