OrgC++ is a package for the complete management of data in C++ programs, based on the new concept of hyper-objects (see below), sometimes also called pattern classes. OrgC++ is much more than just a class library. It provides an additional abstract layer, a whole subsystem for the management of data. You do not have to assemble the organization from library objects. The organization is automatically generated in a way which provides optimum run-time performance. Combined with automatic persistency (storage to disk) and version control, OrgC++ provides a set of powerful tools which are useful for a variety of tasks ranging from the management of internal data to the design of fast memory resident databases.
Compared to usual class libraries, OrgC++ has the following advantages: C
What are hyper-objects? Hyper-objects are objects that store their data in other objects, called carriers. Carriers passively keep the data, but hyper-objects provide the methods. Hyper-objects are a perfect model for the organization of data in computer programs. For example, a graph can be represented as a hyper-object, which keeps its data (pointers forming the graph organization) in objects of type vertex and edge. Operations such as "add" or "traverse" are associated with the graph, not with vertices or edges.
Hyper-objects make the management of data conceptually clean. The new abstract layer removes unnecessary multiple inheritance/ friend declarations from the application code, and maintains optimum run-time performance.
There is a basic conceptual difference between OrgC++ and standard class libraries. For example, classical implementation of the linked list by Stroustrup uses two classes: slink which represents one link of the list, and slist which contains the entry into the list. When we split a list into two sublists, we need two instances of slist, but when we want to place an object into two parallel lists, we cannot easily differentiate between the two lists. Reference [5] in Appendix B explains this in a greater detail.
In OrgC++, when we split a list, the sublists, each with its own instance of slink, remains in the same hyper-object. Two parallel lists are represented as two instances of the list hyper-object.
Individual hyper-classes can be implemented in C++ with multiple inheritance and friend declarations, but an efficient library based on generic functions needs a special manager. OrgC++ supplies this missing link.
OrgC++ consists of a class generator and a special library. The class generator typically reads your header file with the declarations of your objects and organization, and it creates two files: an include file (the default name is zzincl.h), and a source file with all the access routines required by your program (default name zzfunc.c). You compile and link your original source code with these two files. This method has two advantages:
OrgC++ provides a high level of error checking. As a result of strong typing, many coding errors are detected by the class generator or by the compiler. At run-time, OrgC++ protects your program against dangling pointers caused by incorrectly moving or deallocating some objects. It takes slightly longer to get a program through the compiler, but once it compiles, it is much safer to run.
The library contains all standard data objects:
Most of the organizations come in source. The library is open. This means that users may modify the existing organizations and add new ones. All OrgC++ calls are fully encapsulated in C++ classes, and are separated from the application code. A special UTILITIES hyper-object contains general utilities such as allocation routines and saving to disk.
OrgC++ uses generic functions. For example, add() can be used to add an object to a ring, to a tree, or to a graph, without loosing the advantage of full type checking. The user does not have to remember many functions, only a small set of names such as add(), del() for deleting (disconnecting) an object, fwd() for moving forward in a chain of objects, or par() for reaching a parent in hierarchies or trees.
When creating a new object with a regular constructor, all internal data pointers are automatically initialized as "disconnected". When you insert ZZ_CHECK() into a class destructor, OrgC++ prevents objects from being deallocated prior to disconnecting pointers to other objects. Variable length names and other string-type attributes are treated as objects.
Whole organizations of data can be saved to disk with a single command (structure blasting). When reading the data back into memory, all pointers forming the organization, and also all internal virtual function pointers are automatically restored. Binary or ASCII format can be used; the latter permitting the transfer of entire databases between different computers and operating systems.
Note that the C version of Organized C provides the same functionality in a less elegant setting. Hyper-objects there are declared in a very similar manner, but the access calls are macros instead of C++ methods.
For example, the C++ situation
class listHead { ... };
class myObject { ... };
ZZ_HYPER_SINGLE_COLLECT(myList,listHead,myObject);
listHead *hp;
myObject *op;
hp=new listHead; // initialize empty list
...
op=new myObject;
myList.add(hp,op); // add op to myList
is, in C, represented as:
typedef struct listHead listHead;
typedef struct myObject myObject;
struct listHead { ... };
struct myObject { ... };
ZZ_ORG_SINGLE_COLLECT(myList,listHead,myObject);
listHead *hp;
myObject *op;
ZZ_PLAIN_ALLOC(listHead,1,hp);
...
ZZ_PLAIN_ALLOC(myObject,1,op);
ZZ_ADD(myList,hp,op); /* add op to myList */
One of the most important features of Organized C (both in C and C++) is that it unifies programming style between scientific (algorithmic) programming and databases. It allows organized database-like access to be used within algorithmic programs without losing the speed and efficiency of custom coded data and, on the other hand, allows rapid coding of complicated in-core databases which, when combined with hierarchical representation and saving to disk, work for essentially any data size.

The problem involves towns located in different states. Two transportation networks connect the towns: highways and airline connections. The problem is to code a program, which will determine the least expensive route between a given pair of towns. The program will have several parts:
#define ZZmain // declares the main part of the program
#include <<stdio.h>>
#include "ZZinclude.h" // orgC++ class generator will create this file
For each object, only the attributes that do not relate to the organization of data are declared. To make the example easy to read, these attributes such as gasPrice or cost are public even though, in a real application, they would most likely be private. Objects which will be involved in the automatic handling of data must contain a line of the form ZZ_EXT_<objectType>. This statement marks the place where the transparent pointers will be inserted.
class State {
ZZ_EXT_State
public:
float gasPrice;
};
class Town {
ZZ_EXT_Town
public:
float cost; // for the algorithm
struct Town *trace; // route traceback
};
class Highway {
ZZ_EXT_Highway
public:
int distance;
};
class AirLink {
ZZ_EXT_AirLink
public:
float fare;
};
Each line declares one organization as a hyper-class. For example, the first line declares a graph with Towns as nodes, and AirLinks as edges; air is the instance of the GRAPH organization. The words SINGLE and DOUBLE denote singly and doubly linked organizations. AGGREGATE is used when one object type contains several objects of another type. We always use RING to represent a linked-list (we do not recommend the use of NULL ending list). Performance and access are about the same, and RING allows better run-time error checking.
ZZ_HYPER_SINGLE_GRAPH(air, Town, AirLink);
ZZ_HYPER_SINGLE_GRAPH(hwy,Town, Highway);
ZZ_HYPER_DOUBLE_RING(states, State);
ZZ_HYPER_SINGLE_AGGREGATE(byState, State, Town);
ZZ_HYPER_UTILITIES(util);
Once the organization has been declared in this manner, the program can manipulate the data by calling the functions associated with the hyper-objects. states, byState, hwy, and air are instances of organizations (hyper-objects). The save() function starts from a given object, called here "key entry", collects all objects connected to it by the network of pointers, and saves the whole organization to disk.
Often, data organizations require more than one key entry to collect all objects. This is the reason for having char *v[1],*t[1] which, in our simple case of one entry, may appear superfluous.
Highway *hp;
State *sp,*ss;
Town *tp;
char *v[1],*t[1];
tp=new Town; // allocates and initializes new town
byState.add(sp,tp); // adds town tp under state sp
hwy.del(hp); // deletes highway hp from graph "hwy"
ss=states.fwd(sp); // returns next state after sp
states_iterator sIter(ss); // starts iterator from ss
while(sp=sIter++){ // loops through all states
printf("gasPrice=%f\n",sp->gasPrice);
}
v[0]=(char*)ss; /* entry into the data network */
t[0]="State"; /* type of the entry object */
util.save("myFile",v,t,1); // saves whole organization on file "myFile"
The iterators are automatically declared for all organizations specified in the ZZ_HYPER_.. statements. Typically, an iterator traverses the whole set (for example of a ring/list or collection), or a subset: for a tree it traverses the children of a given node, for a graph it traverses the adjacent edges and nodes.
The convenient form of the iterator while(..=..){ is accepted by the AT&T C++ compiler, but ZORTECH and BORLAND compilers issue warning messages. If you want to avoid these messages, use the following statement which does the same thing, but is more difficult to read:
for(sp=sIter++; sp; sp=sIter++){ ...
Changing the organization is easy. For example, we may want to add a town name and a hash table that would allow fast selection of Towns by name. Also, the graph traversing algorithm may need a binary heap. All we need are the following additions:
class Header { // header for arrays and for the hash table
ZZ_EXT_Header
};
ZZ_HYPER_NAME(townName,Town); // town name
ZZ_HYPER_ARRAY(heap,Header,Town*);// heap of town pointers
ZZ_HYPER_HASH(hash1,Header,Town); // hash table
Once this is declared, the new organizations can be used:
Header *hd; Town t,*tp; char *cp; int cmpFun(char *,char *); // compares two objects as for qsort()cp=util.str("name1"); // allocates new string "name1" townName.add(&t,cp); // adds name to temporary town t ... p=hash1.get(hd,&t); // finds Town with this name if(tp){ cp=townName.fwd(tp); // returns name of town tp printf("town=%s\n",cp); heap.inHeap(cmpFun,hd,tp); // inserts tp into the heap }
If you allocate all objects with operator new(), as in the example above, pointers in all objects are automatically initialized as disconnected (NULL). Without a proper initialization, you are likely to run into serious problems with the data.
Objects are not automatically initialized, if you allocate them automatically. In such a case, you must include macro ZZ_INIT() in the constructor of the class. For example if, instead of what we had above:
class Town {
ZZ_EXT_Town
public:
float cost;
Town *trace;
};
Town *tp;
tp=new Town;
...
you write:
class Town {
ZZ_EXT_Town
public:
float cost;
Town *trace;
Town(){ZZ_INIT(Town);}
};
Town tp;
...
It is recommended to be consistent and use the same style for all your classes: Either use ZZ_INIT() in all your constructors, or avoid automatically allocated objects.
| Chapter 6: Examples |