/* * CENA C++ Utilities * * by Stephane Chatty * * Copyright 1991-1993 * Laboratoire de Recherche en Informatique (LRI) * Centre d'Etudes de la Navigation Aerienne (CENA) * * documentation skeleton * * $Id$ * $CurLog$ */ \documentstyle[11pt,mydoc,twoside]{doc} \pagestyle{ENTETE} \makeindex \def\utils{CCU} \namedoc{CENA C++ Utilities} \begin{document} \maketitle \cleardoublepage \tableofcontents \input{psfig} \chapter{Introduction} This documentation describes a number of classes that can be useful for developping C++ applications, and especially user interfaces. The packages are: \begin{itemize} \item a memory manager. As suggested by the C++ language designers it provides efficient allocation for small objects. This memory manager is well suited for the management of fixed size objects. \item dynamic arrays. \item linked lists, with dynamic allocation of list cells. These lists are designed for the simple management of sets of any kind of elements in a linked form, without node sharing. There are two classes, for single and double linked lists. This package also provides list-based implementations of stacks and queues. \item hash tables. Hash tables can be used for building associative arrays. Users can define their own hashing function. A derived class is also provided: it offers ready-to-use association tables with strings as keys, i.e. dictionaries. \item a signal handling package, which makes it possible to disable or handle signals. \item a time handling package. Two classes implement time stamps and wall clocks. Another class, based on the {\it UNIX BSD\/} interval timer, implements alarm clocks and timers. %\item a basic string package. %It is designed in order to avoid many \typ{(char *)} manipulations when using strings. \item a search path package, which mimics and generalizes the \com{PATH} environment variable of {\it UNIX}. \item a regular expression package, that encacpuslates the regular expressions from the C library. \item {\em smart pointers}. Smart pointers are more secure than usual pointers because they manage a reference count of the pointed to data. This makes it easy to manage shared data structures. \end{itemize} \section{Using \utils} \utils\ is installed as: \begin{enumerate} \item the header file \com{ccu.h}, which contains the definitions for classes, functions and constants provided by \utils. \item the directory \com{ccu}, which contains headers for the different packages of CCU. \item \samepage {the archive file \com{libIvl.a}, which is usually instal\-led in \com{/usr/loc\-al/lib}, contains the library procedures. It must be loaded with the object files which use \utils. This is usually performed by adding the flag \com{-lIvl} in the command line for your C++ compiler. For instance, you can type~: \begin{center} \com{CC -o demo demo.C -lIvl} \end{center}} \end{enumerate} \chapter{Memory allocators} \label{Memory management} #class IvlAllocator #class IvlAllocatorOf \section{Allocating small objects} When creating an instance of a C++ class, some memory is allocated for the object, if it is not a static one. That memory is allocated with the allocation mechanism of C++, which one might want to override for small objects. The following fragment of code is an example of this operation for a class \typ{POINT}, using an allocator: \begin{ccode} typedef int COORD; class POINT { private : static IvlAllocator* PtAlloc; COORD X, Y; public : inline POINT (COORD, COORD) : X (x), Y (y) {} void* operator new (int); void operator delete (void*); }; IvlAllocator* POINT::PtAlloc = 0; void* POINT :: operator new (int) { /* Initialize the allocator for POINTs, if not done. */ if (!PtAlloc) PtAlloc = new IvlAllocator (sizeof (POINT)); return PtAlloc->Alloc (); } void POINT :: operator delete (void *that) { PtAlloc->Free (that); } \end{ccode} Note that we do not use the argument of \fun{operator new}, because our allocator always allocates chunks the size of a \typ{POINT}. This will cause severe problems when deriving a class from class \typ{POINT}, if you do not overload \fun{operator new} again. A more secure implementation would check the size argument against the size of chunks allocated by the allocator being used. \chapter{Dynamic arrays} #class IvlArray #class IvlArrayOf \chapter{Lists} In most languages, dynamic sets of elements are often needed. Arrays provide a way to do this, but are inappropriate when these sets vary frequently and randomly, and especially when new elements have to be inserted at any position. The classes \typ{IvlList} and \typ{IvlDList} are designed to manage such sets. \typ{IvlList} implements single linked lists, while \typ{IvlDList} implements double linked lists. Elements of a list can be enumerated by an iterator. The classes \typ{IvlListIter} and \typ{IvlDListIter} provide such iterators. Lists can store objects of different types since the entries of the list are of type \typ{void *}. However you should be careful when using such heterogeneous lists. \section {Simple lists} #iclass IvlList \subsection {Iterating through lists} #iclass IvlListIter \subsection{Generic versions} #iclass IvlListOf #iclass IvlListIterOf \section {Bidirectionnal lists} #iclass IvlDList \subsection {Iterating} #iclass IvlDListIter \subsection{Generic versions} #iclass IvlDListOf #iclass IvlDListIterOf \section {Example} The following example illustrates a common usage of lists. \begin{ccode} String a = "a"; String b = "b"; String c = "c"; ListOf l; l.Append (&b); l.Prepend (&a); l.Append (&c); ListIterOf li (l); while (++li) printf ("%s\n", *li); \end{ccode} \chapter{Hash Tables} #iclass IvlHashTable #iclass IvlHashCell \section{Iterating on hash tables} #iclass IvlHashCellIter #iclass IvlHashIter \section{Strings as keys: dictionnaries} #iclass IvlDictionnary \section{Generic classes} #iclass IvlHashTableOf #iclass IvlHashCellOf #iclass IvlHashCellIterOf #iclass IvlHashIterOf #iclass IvlDictionnaryOf \section{Example} This example is an interactive program to create and use a dictionnary. The keys are entered by the user, and the program assigns the information. \begin{ccode} main () { IvlDictionnary table (16); char line [256]; char *s; int num = 0; IvlHashCell *h; int found; table.Reset (); printf ("? to get help\n"); for (;;) { s = gets (line); if (! s) break; if (strcmp (s, ".") == 0) break; /* help */ if (strcmp (s, "?") == 0) { printf ("name add name\n"); printf ("-name delete name\n"); printf ("# dump table\n"); printf ("%% statistics\n"); printf ("> rehash into smaller\n"); printf ("< rehash into larger\n"); printf (". quit\n"); continue; } /* stats, dump */ if (strcmp (s, "%") == 0) { table.CollStats (1); continue; } if (strcmp (s, "#") == 0) { HashCell* ce; HashIter iter (table); while (++iter) { ce = *iter; printf ("%s\n", ce->GetKey()); } continue; } /* rehashing */ if (strcmp (s, ">") == 0) { int s; s = table.GetSize() / 2; if (s < 1) { printf ("too small\n"); continue; } printf ("rehashing into smaller table : size %d\n", s); table.SetSize (s); continue; } if (strcmp (s, "<") == 0) { int s; s = table.GetSize () * 2; printf ("rehashing into larger table : size %d\n", s); table.Resize (s); continue; } /* delete */ if (*s == '-') { void* p; p = table.Remove (++s); if (! p) printf ("%s not found \n", s); else printf ("%s had info %d\n", s, (int) p); continue; } /* add */ table [s] = (pointer) num++; // the info is a simple number } } \end{ccode} \chapter{Identifier tables} #iclass IvlIdTable #iclass IvlIdIter \chapter{Signal management} #class IvlSignalBlocker #class IvlBaseSignalHandler #class IvlSignalHandler \chapter{Time management} \section{Measuring time} \utils\ provides two classes for measuring time. The class \typ{IvlTimeStamp} provides a mean of dating events with reference to an absolute clock (the standard dating scheme of Unix), whereas the class \typ{IvlTime} makes it possible to measure time intervals. #iclass IvlTimeStamp #iclass IvlTime \section{Timers} #iclass IvlCoreTimer \subsection{Signal-based timers} #iclass IvlBaseTimer #iclass IvlTimer \subsection{Example} This example creates three real time timers: the first one counts seconds, the second one counts minutes, and the third one counts three minutes once. The third timer stops the two other ones when it expires. Section \ref{Queues example} shows another example where a timer is used. \begin{ccode} void tick () { write (1, ".", 1); // write a dot each second } void ding () { write (1, "\n", 1); // change line each minute } void done () { printf ("\nDONE!!\n"); exit (1); } main () { IvlTimer seconds (1000, tick); IvlTimer minutes (60000, ding); IvlTimer eggs (done, 180000, 0); // wait for the eggs ... // usual programs do other things meanwhile for (;;) pause (); } \end{ccode} \subsection{Deriving new kinds of timers} If you have another source of interruptions than signals and you want to map timers on it, you will need to derive a new class from \typ{IvlCoreTimer}. The derived class must implement the functions \fun{StartAlarm} and \fun{StopAlarm}. Its constructor must also provide a timer set to the constructor of \typ{IvlCoreTimer}. This timer set will hold all the timers of the new class. It will belong to the class \typ{IvlTimerSet}. #iclass IvlTimerSet \chapter{Search paths} #class IvlDirPath \section{Example} The following example looks for several files in a search path made of the user's binaries directory and its current path, with \com{/etc} excluded. This example illustrates the use of the allocation mode with respect to destroying the returned string. \begin{ccode} IvlDirPath path; path.Append ("."); path.AppendEnvPath (getenv ("PATH")); path.Remove ("/etc"); const char* file1 = path.Find ("my_file"); const char* file2 = path.Find ("other_file"); /* file1 now refers to garbage */ const char* file2 = path.Find ("other_file", true); path.SetAlloc (1); char* file3 = path.Find ("my_file"); FreeString (file1); // crash ! FreeString (file2); // safe FreeString (file3); // safe (default allocation now true) \end{ccode} \chapter{Regular expressions} #class IvlRegExp \section{Example} This procedure matches a string against a regular expression. \begin{ccode} int match (const char* exp, const char* s) { IvlRegExp re (exp); if (!re.Compile ()) { printf ("Cannot compile expression \"%s\"", exp); return 0; } return re.Match (s); } \end{ccode} \chapter{Smart pointers} The classes described here implement what we call smart pointers. Usual C++ pointers have two major flaws. The first one is that you can delete an object even if there are pointers to it: \begin{ccode} class A; A* p = new A; A* q = p; delete p; q->something(); // bug \end{ccode} Dereferencing such pointers will have unpredictable consequences. The second problem is the opposite: a dynamically allocated object can become unreachable and will never be destroyed, wasting memory space. \begin{ccode} class A; A* p = new A; p = 0; // original object now unreachable \end{ccode} Smart pointers eliminate both problems, but of course with some run-time overhead. #class IvlSmartData #class IvlSmartPointerTo \section{Example} This short example shows how to declare a class and a smart pointer class to it. It also illustrates how smart pointers and usual pointers can be mixed. \begin{ccode} class OBJ : public DATA { private: STRING name; public: OBJ () : name ("?") {} OBJ (char* n) : name (n) {} ~OBJ () { printf ("%s says bye\n", name ()); } void print (char* m) { printf ("%s: %s\n", m, name ()); } }; PointerClass (REFOBJ, OBJ); // this declares the smart pointer class OBJ* fun (OBJ* o) { o->print ("fun(o)"); return new OBJ ("fun"); } main () { REFOBJ ref1 = new OBJ ("foo"); REFOBJ ref2 = new OBJ ("bar"); REFOBJ ref3 = ref2; printf ("start\n"); ref1->print ("ref1"); ref2->print ("ref2"); ref3->print ("ref3"); printf ("\nref3 = ref1\n"); ref3 = ref1; ref1->print ("ref1"); ref2->print ("ref2"); ref3->print ("ref3"); printf ("\nref2 = ref1\n"); ref2 = ref1; ref1->print ("ref1"); ref2->print ("ref2"); ref3->print ("ref3"); printf ("\nref2 = fun (ref1)\n"); ref2 = fun (ref1); ref1->print ("ref1"); ref2->print ("ref2"); ref3->print ("ref3"); printf ("\nthe end\n"); } \end{ccode} This program produces the following (reformatted) output: \begin{ccode} start ref1: foo ref2: bar ref3: bar ref3 = ref1 ref1: foo ref2: bar ref3: foo ref2 = ref1 bar says bye ref1: foo ref2: foo ref3: foo ref2 = fun (ref1) fun(o): foo ref1: foo ref2: fun ref3: foo the end fun says bye foo says bye \end{ccode} \com{bar} has been deleted as soon as it has been unreachable. \com{fun} takes a pointer as argument and returns a pointer. However it is called with a smart pointer and its value is assigned to a smart pointer. Finally the two object \com{fun} and \com{foo} are deleted when returning from main. \newpage \appendix \chapter{Class List} This chapter contains the list of the classes defined in \utils. The first section contains the inheritance tree of \utils\ classes. The second section contains for each class the ordered list of its base classes. The section number indicated after each class refers to the documentation of that class. Classes defined in \utils\ but which are not documented do not appear in the lists. \section{Inheritance Tree} This section contains the set of classes defined in \utils. Each base class is followed by the indented list of its subclasses. \input{inhtree.tex} \newpage \section{Inheritance List} This section contains the set of classes defined in \utils. Each class is followed by its base class, recursively. Thus, from a given class, one can follow the inheritance link and thus refer to the documentation for the inherited methods. \begin{inhlist}{XXXXXXXXXXXXXXXX} \input{inhlist.tex} \end{inhlist} \begin{theindex} \indexinc \end{theindex} \end{document}