#if 0 Revised - RJL041207: I used #if 0 ..#endif brackets to comment out all but the macro defs and calls. Then I used gcc -E to expand the macro bodies calls. RJLRef: $PH/04f522/exam2/pr_set_key_macros.txt (linked to $PH/COOL-GEN/pr_set_key_macros.txt) I extracted from files in $RL/pr_util to answer some exam2 questions by RAlmonte. The same info is generated by chgen for every application. The learning curve for a fremework component like COOL-GEN and its API requires a big investment by its users. This is cost-effective when amortized over many applications. R Lechner ------------------------------------------- > From Robert_Almonte@student.uml.edu Tue Nov 30 14:27:35 2004 > From: "Robert Almonte" > To: Bob Lechner > Subject: Re: question about problem 4 > In the code below, I would like to know the > parameter meanings, and what type is it? > ca =? > pa=? > p=? > c=? > > What does this variable (pa##temp) compile to? I mean the ##. > > #define unlink_parent_nobp_m(ca,pa,p,c) \ > pa##temp = ca##curr->p##_pp; \ > ca##temp = ca##curr; /* Change CEM & RJL 20 APR 93*/ \ > if (pa##temp != NULL) \ > { \ > if (( pa##temp->c##_fcp == ca##temp) && \ > ( (struct pa *) ca##curr->p##_fpp == pa##temp)) /* only child > case */ \ > { \ > pa##temp->c##_fcp = NULL; \ > } \ > else \ > if ( pa##temp->c##_fcp == ca##temp ) /* first child case */ \ > { \ > pa##temp->c##_fcp = (struct ca *)ca##curr->p##_fpp; \ > ca##curr2 = (struct ca *)ca##curr->p##_fpp; \ > } \ > else \ > if ( (struct pa *) ca##curr->p##_fpp == pa##temp) /* last child > case */ \ > { \ > (struct pa *) ca##curr2->p##_fpp = pa##temp; \ > } \ > else /* > middle of child chain > case */ \ > { \ > ca##curr2->p##_fpp = ca##curr->p##_fpp; \ > ca##curr2 = (struct ca *)ca##curr->p##_fpp; \ > } \ > } > > and > link_parent_nobp_m(ca,pa,cf,pk,p,c) > I would like to know the type and meaning of > ca=? > pa=? > cf=? > pk=? > p=? > c=? > > Sincerely yours, > Robert Almonte > ------------------------------------------------------ The operator '##" is fundamental to code generation in C via generic macros; It is explained at the start of any schema.h file from chgen): (e.g., at line 38+ of $RL/pr_util/94sbde_schema.h): This is followed by the macro #define hcg_declare(tbl) *tbl = NULL , ... which declares global (table-specific) symbols that are used throughout pr_util. =========================================================================== #define CHGEN_VERSION "Chgen V 12 - Sathya" #define CHGEN_RELEASE_DATE "Thu Feb 21 19:43:51 GMT 2002" #define CHGEN_QUALIFIERS " -ansi -nobp" /******************************************************************************/ /* NOTE: The symbol ## found throughout this include file is called the */ /* identifier concatenation symbol. This symbol is ansi 'C' specific, */ /* and its equivalent in K&R 'C' is the empty comment (which cannot */ /* be typed here, to avoid nested comments). By using this symbol, */ /* macros are able to construct larger identifier names out of smaller */ /* ones. For example, given a table name AA, the AAcurr variable can */ /* be 'referenced' by concatenating AA with curr. This concept is */ /* exploited throughout the macros in this system. */ /******************************************************************************/ /******************************************************************************/ /* This macro will either declare or define the table variables for each */ /* table in the schema. This general approach (#ifdef MAIN) is used often */ /* to distinguish between a main module and a non-main module. In a main, */ /* a definition can occur, and variable initialization can occur. In a non- */ /* main, only a declaration can occur, so the word extern is used. Although */ /* VAX 'C' can handle redundant definitions, several versions of 'C' for UNIX */ /* cannot. */ /******************************************************************************/ #ifdef MAIN #define hcg_declare(tbl) *tbl = NULL , *tbl##curr = NULL , *tbl##temp = NULL , *tbl##end = NULL , *tbl##elt = NULL,\ *tbl##curr2 = NULL , *tbl##temp2 = NULL #else #define hcg_declare(tbl) *tbl, *tbl##curr, *tbl##temp, *tbl##end, *tbl##elt, *tbl##curr2, *tbl##temp2 #endif ========================================================================= The simplest pr_link macro is appended below, as extracted from line# 2114 of $RL/pr_util/pr_load.c (this line# depends on .sch file size). Mentally constructing actual argument names and types is easy, once you know the meaning of '##' to the C preprocessor. However, read sample CALLS to a macro to avoid guessing. E.g. the pr_link macro in pr_load.c makes this call: link_child_nobp_m(CG,HG,HGid,HGid,HGid,CGid); and the macro body constructs multiple identifier refs from these args. (Reading the code example in the gen user guide is also quite useful :-) When debugging (the crazy formating punctuation in particular) you can expand a macro body by using the -E option of gcc or g++: then you can see the expanded source text (in file type .E). Unfortunately chgen v6 authors did not pick datatype-specific naming conventions and follow them consistently. Therefore variable types must be inferred from the macro body context, or earlier definitions. The effort is wortn-while since this knowledge can be amortized over ALL applications of chgen (all pr_util libraries). These pr_[un]link macros are NOT intended for ue by the application programmer. They are hidden inside client methods (pr_set_key in this case) If you read the lcp/olc source code you know an alternate way to generate get and set macros. It would pay off if alternate macros for linked list maintenance could also follow olc's example. (That would be a good 04f523 project.:-) ------------------------------ #endif /******************************************************************************/ /* This private macro will loop over the specified child table, linking the */ /* first child it finds for this parent row. This is acceptable since it is */ /* supposed to be a singleton relationship. */ /* The macro aborts if more than one child is linked in - RJL 94/12/6 */ /* assert (loosely) allows NULL or self-pointer in the current parent */ /******************************************************************************/ #define link_child_1(ca,pa,cf,pk,p,c) \ assert(pa##curr->c##_fcp == NULL || pa##curr->c##_fcp == pa##curr);\ pa##curr = pa##elt; \ ca##curr2 = ca; \ while(ca##curr2) \ { \ if (key_compare(&ca##curr2->cf,&pa##curr->pk) == 0) \ { \ ca##curr2->p##_pp = pa##curr; \ pa##curr->c##_fcp = ca##curr2; \ break; \ } \ ca##curr2 = ca##curr2->next_ptr; \ } ------------------------------ Sample call (for bde extended with generic text block XB (a superclass of CG). #endif link_child_1(CG,XB,XBid,XBid,XBid,CGid); //(adapted from: link_child_nobp_m(CG,HG,HGid,HGid,HGid,CGid); #if 0 ) Evidently pa and ca are table types, cf and pk are child fkey and parent pkey field names, and p and c are prefixes to the child's parent pointer p##_pp and the parent's first-chid pointer c##_fcp respectively. Not so obviously, pa##elt is a global ptr to the HG or XB parent instance (a current child pointer), and ca##curr2 = ca is a global ptr to the beginning row of table ca. hcg_declare(tbl) is responsible for another confusing choice of names - e.g. it declares beginning-of-list and end-of-list ptrs or iterators CG and CGend - these should have been declared as CGbegin and CGend. Then link_child_* could have initialized ca##curr2 to CGbegin by ca##curr2 = ca##begin. The right place to fix these naming conventions is in chgen itself. Exposing them to the API libary in pr_util.a would require changing all their references, if any. This is the best argument for encapsulation (NOT exposing link_child_* calls or the data fields themselves to referencing by the application programmer). C++ classes from gencpp can encapsulate similar methods by making them (and all data members) private or protected. #endif