If you are just starting with OrgC, skip this entire chapter. You may need this information later, but only if you decide to add functions to the library.
There is a big difference between adding new features to OrgC++, which is based on C++ classes, and to OrgC, which is based on a combination of macros and compiled functions. In OrgC++, all functions related to the same organization are in one file, which describes the hyper-class of the organization. In OrgC, each macro or compiled function has its own file. The equivalent of hyper-classes is implemented through tables stored in the file zzmaster.
If you look at any of the files in orgC/macro/hyp*, you will see that numerous ZZ-prefixed macros are used inside the hyper-class declarations. This may create the false impression that the hyper-classes are not coded in pure C++, and that they are only C functions called through a C++ interface.
These macros do not have to be there. If you take the macros from whatever file they are, and insert them into the hyper-class definition, you will get "normal" C++ code. The reason for using the macros is to maintain compatibility between C and C++ versions of Organized C, and also to simplify maintenance of the whole system. When you add a new hyper-class, you don't have to use ZZ-macros, except for two or three macros that create the pointer and type names. (ZZFP(), ZZFN()).
When you want to add a new organization to OrgC++, you have to do two things: You plan the organization and register it in orgC/macro/zzmaster (Chap.16.1); then you code the hyper-class and access functions (Chap.16.2).
Decide how many transparent pointers and other variables the new organization will need, then edit the file orgC/macro/zzmaster, find the end of the section that starts with ZZorganization, and append one line there. The line must contain the following information:
Add pointers/variables required for the organization at the end of the ZZpointer section. Each pointer/variable needs the following fields:
For example, if you plan to add ZZ_HYPER_NEW_ORGANIZATION (id,type1,type2), then a pointer which is on object type1 and points to object type2, will set toUse=1, pointTo=2, while a pointer on object type2 and pointing to a character string (char *), will have set toUse=2, pointTo=0.
If you look into the actual file zzmaster, you will see that some pointer/variable names do not fit the recommended 3 character pattern, for example ZZprop. These pointers belong to hardwired organizations, which are subject to special rules.
The OrgC++ library contains SINGLE_TRIANGLE. Assume that you want to work with a hierarchy, where each level forms a doubly-linked ring, and you want to introduce a new organization called DOUBLE_TRIANGLE.
The organization will be declared by
ZZ_HYPER_DOUBLE_TRIANGLE(id,topType,botType);
and your original zzmaster file looks like this:
/* ind organization p1 p2 nInp */ ZZorganization { 0 SINGLE_RING 0 0 2 ...................... 12 LIFO 20 20 2 13 SINGLE_TREE 21 23 2 }/* ind usedOn pointTo arrSize Type ptrCode */ ZZpointer { 0 1 1 1 a ZZf ................... 22 1 1 1 a ZZc 23 1 1 1 a ZZs}
You will add the new organization (index 14), with 4 new pointers: ZZp=parent, ZZc=child, ZZf=forwardSibling, ZZb=backwardSibling.
For example, the parent pointer is on the topType(2) and points to the topType(1), ZZp has usedOn=2 pointTo=1.
The new zzmaster file:
/* ind organization p1 p2 nInp */ ZZorganization { 0 SINGLE_RING 0 0 2 ...................... 12 LIFO 20 20 2 13 SINGLE_TREE 21 23 2 14 DOUBLE_TRIANGLE 24 27 3 }/* ind usedOn pointTo arrSize Type ptrCode */ ZZpointer { 0 1 1 1 a ZZf ................... 22 1 1 1 a ZZc 23 1 1 1 a ZZs 24 1 2 1 a ZZc // child 25 2 1 1 a ZZp // parent 26 2 2 1 a ZZf // forward 27 2 2 1 a ZZb // backward }
In your new organization, plan on using a ring-type arrangement where valid pointers cannot have a NULL value. If you do so, your program will be better protected at run-time.
The whole hyper-class will be in one file. All files containing hyper-class C++ definitions start with hyp.... You may follow the same convention, or use a special prefix, for hyper-classes which you designed yourself. The file, as with all files in the macro directory, will contain both the documentation and the computer code, separated by special comment lines:
/* ========================================= */
... documentation ...
/* ========================================= */
... first part of the macro,
which also will appear
in the documentation ...
/* +++++++++++++++++++++++++++++++++++++++++ */
... remaining part of the macro ...
/* _______________________________________________ */
... compiled functions ...
The first line must be /* ====... , and not a blank line, otherwise the program treats the file as encrypted, and produces complete gibberish.
Note that the separation mark /* __... uses the underscore and not dash.
If your hyper-class uses only in-line functions, the last separation line and the last section can be omitted.
Note that the matching between the library and hyper-class declarations in your program will be done by the class generator (preprocessor). For example, if you declare
ZZ_HYPER_DOUBLE_TRIANGLE(myTriangle,topType,botType);
the file zzincl.h will contain the following statements:
#define ZZ1myTriangle topType #define ZZ2myTriangle botType #define ZZ_EXT_topType \ .... botType *ZZcmyTriangle;\ ....#define ZZ_EXT_botType \ .... topType *ZZpmyTriangle;\ botType *ZZfmyTriangle;\ botType *ZZbmyTriangle;\
zzincl.h gives you access to macros that paste arguments:
| form pointer: example: |
ZZFP(A,B) ZZFP(ZZf,myTriangle) |
creates AB creates ZZfmyTriangle |
| form type: example: |
ZZFT(A,B) ZZFT(1,myTriangle) |
creates ZZAB creates ZZ1myTriangle= topType |
| nameToString: example: |
ZZ_STRINGIT(A) printf("%s/n",ZZ_STRINGIT(myTriangle)); |
creates "A" |
You can find the pasting macros in orgC/lib/heading.h. Most OrgC macros (for use in C) are coded in two levels. The first macro (ZZ_..) sets up pointer and type names; the second macro (ZZZ_..) does the actual calculation and uses simple and meaningful names for variables and pointers. All this name conversion is not needed in C++.
Continuing the example from the previous chapter, we will present segments of the hyper-class for DOUBLE_TRIANGLE. The hyper class will be coded in the form of a parametric macro, stored in the file hypdtria. OrgC++ uses file names with 8 characters or less to maintain portability between UNIX and PC DOS. For the same reason, there should be no special use of upper/lower case in the file name.
/* ====================================== your documentation in nroff -me format, including: definition of parameters description of both the organization and functions possible errors or exceptions example ====================================== */ #define ZZ_HYPER_DOUBLE_TRIANGLE(id,pType,cType) \ /* +++++++++++++++++++++++++++++++++++ */ \ class ZZFP(ZZH,id) { \ friend class pType;\ friend class cType;\ public: \ cType *fwd(cType *s){return(s->ZZFP(id,ZZf);};\ cType *bwd(cType *s){return(s->ZZFP(id,ZZb);};\ pType *par(cType *s){return(s->ZZFP(id,ZZp);};\ cType *child(pType *p){cType *c;\ c=p->ZZFP(id,ZZc); return(fwd(c))};\ .... void del(cType *p);\ };\ ZZ_EXTERN ZZFP(ZZH,id) id; \class ZZFN(id,iterator){ \ cType *start;\ cType *nxt;\ public:\ ZZFN(id,iterator)(pType *p)\ {nxt=NULL; if(p)start=id.child(p); else start=NULL;};\ cType* operator++(){cType *p; if(nxt==start)p=NULL;\ else {if(!nxt)nxt=start; p=nxt; nxt=id.fwd(nxt);} return(p);};\ cType* operator==(){cType *p; if(nxt==start)p=NULL;\ else {if(!nxt)nxt=start; p=nxt; nxt=id.bwd(nxt);} return(p);};\ }; /* ______________________________________ */void ZZH$ :: del(ZZ2$ *t){ if(t && t->>ZZp$){ // if not connected, do nothing if(t->ZZf$==t)t->ZZp$->ZZc$=NULL; // only child else { if(t->ZZp$->ZZc$==t)t->ZZp$->ZZc$=t->ZZb$; t->ZZb$->ZZf$=t->ZZf$; // relink neighbours t->ZZf$->ZZb$=t->ZZb$; } t->ZZp$=NULL; // disconnect parent t->ZZf$=t->ZZb$=NULL; // disconnect neighbours } }
Note several important details of how to deal with pointer/type names:
In the first part (class and iterator declarations which are included through the zzincl.h file) two macros are used: ZZFP(id,ptrName) creates the actual pointer name from the ptrName registered in the zzmaster file by concatenating the two names. ZZFN(id,name) concatenates the two names with an underscore (_) in the middle, and that is used here to create the name of the iterator class.
In the second part (compiled functions which are deposited into the file zzfunc.c), the $ character is used to parametrize the pointers and types. The class generator will replace ptrName$ by the name of the actual pointer, ZZ1$ by the type of the first parameter (pType here), and ZZ2$ by the type of the second parameter (cType here).
The parametrization logic is simple, and the meaning of the pointers easy to follow. Classes coded in this style cause no special difficulties during debugging.
Note that if the C implementation of the DOUBLE_TRIANGLE already existed, we could take a shortcut and code the del() function like this:
ZZH$ :: del(ZZ2$ *t){ZZ_DELETE_DOUBLE_TRIANGLE($,NULL,t);}
which would have no impact on code complexity, run-time performance, or the purity of C++ implementation.
Anywhere in the section of ZZfunction (here the index is not important) enter the following line:
ZZhyp2Tri ZZ_HYPER_DOUBLE_TRIANGLE hypdtria 13 -1 h
where
ZZhyp2Tri is the short name for the macro;
ZZ_HYPER_DOUBLE_TRIANGLE is the long name;
hypdtria is the file where the macro is stored;
13 is the organization index;
-1 is the index into the list of support files, -1 mean none;
h is the type of macro/function, here h=hyper-class.
For hyper-classes you do not need any support files.
If you need a general utility function which does not relate to any particular organization, add it to the UTILITIES hyper-class (file hyputil).
OrgC++ contains three special hardwired classes: SELF_ID, TIME_STAMP, and PROPERTY, which are neither regular base classes nor hyper-classes. Each may appear on any object not more than once and, with the exception of PROPERTY, the variables that form them need special treatment. All three organizations are associated with the object itself (just like a base class), and have no hyper-class id.
For example:
class Block {
ZZ_EXT_Block;
...
};
ZZ_HYPER_TIME_STAMP(Block);
Block *bp;
...
bp->setTime();
You cannot add such special classes to the OrgC++ library. However, since only the object itself is involved, you can achieve the same effect by using plain inheritance outside of OrgC++.
After you introduce a new organization or a new function, the file orgC/zzcomb.h must be updated. When you run zzprep hyper-class declarations are taken from this file and not from individual files in the orgC/macro directory. The file zzcomb.h combines all macros stripped of comments. The file zzfunc.c is created any time you run the class generator.
To update zzcomb.h, do this:
cd orgC
zzcomb
If you forget to update zzcomb.h, your first run of zzprep will take a long time. The program detects that zzcomb.h is out of date, and invokes the zzcomb run automatically.
When you want to add/modify a function to an existing hyper-class, simply modify the file orgC/macro/hyp..., and re-run zzcomb in the orgC directory.
In some situations, you may want to move some functions into a compiled library. For example, you may have to hide (for commercial reasons) the critical part of your code from the programmers who will use the class. Using the library will also avoid unnecessary re-compilation of already tested code, when debugging the program.
This is how to add a new function to the library:
In UNIX:
cc -c newFun.c
ar r zzlib.a newFun.o
ranlib zzlib.a
In DOS (TurboC++):
tcc -mm -c -Lc:\turboc\startups;c:\turboc\lib -Ic:\turboc\
include newFun tlib zzlib /C -+ newFun
The most convenient way to debug a new hyper-class is to run zzcomb and the class generator once, and then continue working with zzcomb.h and zzfunc.c, making modifications directly in them until everything works as expected. The file zzfunc.c has no macros inside, and is easier to read. You can recognize the meaning of all the pointers by the beginning of their names. If you have problems with the parametric definition of the hyper-class in zzcomb.h, replace the pasting macros, temporarily, by actual names. This is very simple, for example, if you have id=myRing, then ZZFP(id,ZZf) becomes ZZfmyRing, and ZZFN(id,iterator) becomes myRing_iterator.
When you finish debugging, recode orgC/macro/hyp...back into parametric form, rerun zzcomb, and re-test the whole setup.
There are several serious reasons why OrgC++ implements hyper-objects through parametric macros. Templates introduced with C++ Ver.2.1 are limited to the use of type only, and do not allow argument pasting which provides close interaction between application code and class generator.
The simple linkage between a hyper-object library and user's code cannot be implemented with existing C++ tools, and we believe that our approach of using macros is more practical than writing a new C++ compiler which would compile data structures directly as a part of the language.
Watch for some typical errors when coding macros:
You need "\" at the end of each line (except for the last line) in the declaration of the hyper-class and (possibly) in the iterator.
Remember the different style of parametrization in the macro part: ZZFP(id,ZZf), compared with ZZf$ in the function part.
You have to generate one global instance of the hyper-class, by using ZZ_EXTERN ZZFP(ZZH,id) id; this line generates an instance of the hyper-class in your file which has #define ZZmain, and only an extern reference in all other files.
Use #ifdef and #endif around whole macros, not just for sections of macros. For example this works fine:
#ifdef UNIX
#define myMacro(a){\
a=a+3; \
}
#else
#define myMacro(a){\
a=a+2; \
}
#endif
but this is a problem with most compilers:
#define myMacro(a){\
#ifdef UNIX
a=a+3;\
#else
a=a+2;\
#endif
}
The C version of Organized C (see the green manual, also called OrgC) allows the creation of one organization from another through hierarchical macros. For example, a TRIANGLE can be derived from a RING, and so on. At the beginning, when OrgC worked only with C, this was considered to be an important feature, similar to C++ inheritance. It is interesting that, over two years of industrial use, hardly anybody has used these hierarchical macros. Perhaps the data objects for basic data organizations are simple enough to be treated individually. Perhaps, the flat model is easier to maintain. Or, perhaps, run-time performance, which is the ultimate objective for a library like this, is easier to tune without hierarchy.
If you want to create organizations hierarchically in C++, we suggest that you use the inheritance mechanism. If you are just curious about the original hierarchical macros, look at Chap. 17.6 in the Users Guide for "Data Manager for C".
| Chapter 17: Merging and Reducing Libraries |