RJLRef: $PH/COOL-LCP/LCPupdateInfo051015.txt [Rev. 051015] This supercedes LCPupdateInfo050927.txt and LCPupdateInfo051013.txt. It has more info on LCP architecture including upgrades to pr_find and to get/set access methods from chgen13. It proposes new extensions to chgen13 and lcp04s/ajlopez/lcp which support multiple classes and methods per class in an encapsulated way, independent of the order of creating State Models via StateModelCreateCompletely. $PH/COOL-LCP/lcp04s is now linked to $CASE/04s522/lcp04s/ That path contains a short aareadme file and an updated narrative 04s522LCPupdateProject.txt. which explains the evolution of LCP's olc architecture up thru the LCPupdate project of ajlopez in 04s522. I intend to merge that file into this one The 04s522/lcp04s/LCPupdate project split LCP for good: LCPsetup.c (setup phase) creates the FuncTbl[] array of Action routine pointers as lcp/include/HMfuncptr.h. HMfuncptr.h is then compiled and linked into LCPmain.c (run phase) which gets the function address at the index stored in field ActRtn of State table ST in the State Model database, LCPsetup.c is the textual work-around for creating the SM's database. This SMDB-creation process involves defining tables ST, TR and ET as arrays of string constants. It avoids the need for bde diagrams and bde2SM conversion. It enables the reverse process: SM2bde conversion and GUI-editing to layout the diagram with BDE. Examples of LCPsetup (not SM2bde) are in the Hominid and setGame projects, as well as the original JuicePlant demo JPsim. (The setup file for state models of table or class XX by convention is named XXsetup.c.) An example is given and discussed in file TBD_____________________ $PH/COOL-LCP/lcp04s/ajlopez/lcp/src/*setup.c declare and initialize state models for the Hominid project table types. This project started in $CASE/04s522. $PH/04f522/04f522HominidPathGenAsgnt* has 24 .ppt slides that explain this project and conclude with extensions (interactive path generation) that could be implemented in 05f523. $CASE/03f522/setGameSetup.c has tentative setup files <<<<<<<<<<< m $PH/projects/Hominid04s/lcp/include/HMfuncptr.h void (*FunctTbl[])= { HdAction0 ,HdAction1 ,HdAction2 ,HdAction3 ,HlAction0 ,HlAction1 ,HlAction2 ,HlAction3 ,HlAction4 }; ---------------------- HMfuncptr.h is a post-condition of LCPsetup (LCPsetup.c writes this file), and a pre-condition of LCPmain (only LCPmain.c #includes it): -------------- saturn.cs.uml.edu(154)> grep HMfuncptr *.c LCPmain.c:11:#include "HMfuncptr.h" --------------- Files HDheader.h and HLheader also refer to {Hd,Hl}action#s. ajlopez merged these into new HMfuncptr.h. The separate files (a legacy from Team1) are obsolete and no longer #included in lcp04s/lcp/{olc,src,pr_util}/*.c. [TBD: SPlit them again, and add multi-level indexing - see below).] Ajlopez' extended Team1's Hominid project by having LCPsetup.c create HMfuncptr.h. This file is used in doTransition() below, which is called by ProcessOneEvent() in olc/processevent.c. In Team1's former version of olc, doTransition searched table XXheader.h for a match to STcurr->Actname to get the Action address. Now the call to method STcurr->Actname is trivial: (1) Call ActiveInstanceSetState(AIid, StateGetName(STid)); (2) get the function address p from FunctTbl[STcurr->ActFunc]; (3) call (*p)(EIid) passing the EventInstance pkey argument. [Currently, STcurr comes from pr_find(ST, STid, encode(AIcurr->STid), because AIcurr->STid has type c8 with is_key = 0. TBD: Fix chgen13 to support pr_set_key and set is_key = 1 tp make AIcurr->STid an fkey and use (STcurr = AIcurr->STid_pp).] ST has these non-key fields: Actname, ActFunc, Name (State name); [Ref: Slide 7 of $PH/COOL-LCP/StateModelRev05f.{PPT,HTM}]: StateModel.sch File (incomplete): --------------- SchemaVersion SV /* schema version containing this state mode */ { /*SV Attributes: name, versionNo, lastModAuthor, lasModDate*/ } SchemaVersionSV /* particular version of a schema - include NameSpace-RJL051015 */ { SVid NA c8 1 /* fkey of Schema Version */ Abbrev NA i4 0 /* ttabbrev of Modeled Object? */ Name NA t80 0 /* Full Name or Description of SM */ } State ST / * One state or stage of behavior ( TT000002 ST) */ { STid NA c8 1 /* pkey of this STat e */ SMid NA c8 1 /* fkey of parent State Model */ STname NA c32 0 /* a name to identify this state */ ARname NA c8 0 /* Action Routine Name */ ARptr NA i4 0 /* Was pointer, now integer index since 04s522) */ Descrip NA t80 0 /* Full Name or Description of State */ } --------------- $PH/COOL-LCP/StateModelRev05f is old. lcp04s vrsion below: From $PH/COOL-LCP/lcp04s/ajlopez/lcp/schema/Hominid.msdat: ActiveClass AC /* The Active Class */ { ACid ActiveClassId c8 1 /* the primary key */ SMid ActiveClassesStateModel c8 1 /* the state model for this class */ Name ActiveClassName t80 0 /* the name of the Active Class */ } ST State /* states */ { STid StateId c8 1 /* the primary key */ SMid StateAStateOfThisStateMachine c8 1 /* The state machine */ ActName StatesActionRoutineName t32 0 /* Action Routine Name */ ActFunc StatesActionRoutinePointer i4 0 /* C pointer to function */ Name StatesName t80 0 /* The name of the state */ } New Refactoring Needs: To expedite proof-of-concept above, ajlopez in lcp04s bypassed several levels of encapsulation. TBD: prepare for future encapsulation by gencpp of code within each Active Class (Entity Type in the application data model). New requiremens include: (1) Refactor lcp04s/olc code so XXFUnctTbl[] arrays are declared separately and independently for each Active Class XX. In class AC, add an index field to define an offset within new array ACFuncTblBase[] that holds FunctTblBaseAddress for each State Model method within the AC. (Diffrent SMs may exist for different methods with possibly nested invocation. [E.g., the bdeState project which used CHGEN V10.2 in: $CASE/98f/bdeStates/achallur/chgen/executables/${HOSTTYPE}/chgen10.2 and extended the BDE[SYM?] version $CASE/97f522/bde2java/hhuang/bde/executables/${HOSTTYPE}/bde. This project was replaced by a more general one in ________ The new bdeState model defines one SM to select a class and another to select a method, because bde's left-side OOMenu is split into two radio button sets). All *ops.cc state-based methods are nested state models. TBD: Insert this new bdeState model within bde with genv13.] TBD1: StateModelCreateCompletely needs to declare its own XXFUnctTbl[] and add the index STcurr->ActFunccorresponding to the ActionRoutine function named ActName for each state of this SM. This will provide unique index to access one action routine for each STid (state) of this SMid (method). TBD2: To expedite access to the base address of XXFUnctTbl[], LCPsetup needs to declare a global array ACFunctTblBase[] (one entry per AC) and StateModelCreateCompletely must insert the address of its own XXFUnctTbl[] there. TBD2: generate a separate XXFunctTbl[] array for (each or all?) methods for all SMid children of the AC superclass instance (to which the class XX delegates flow control). [Note: this is class data for ActiveInstances corresponding to rows of table XX.] Each XXheader.h file contains pointers to methods of its ActiveClass. (LCP with gencpp should declare these as class methods in XXops.cc. In lcp04s with pr_accessors.c from chgen13, HDheader.h and HLheader.h could be reborn with this content.) DoTransition must know the base address of the specific FuncTbl array for each AC and SM. The STcurr->ActFunc index accesses a function pointer at FunctTbl[index] within this array for this SM. TBD: Rename FunctTbl to SMFunctTbl; add a field FuncTblBaseIndex to AC as an index into global array FunctTblBase[] of base addresses &XXFunctTbl[] For DoTransition, XX is HD or HL. During LCPsetup, indices are set in this field: HDsetup calls ACSetFuncTblBaseIndex(HDid, 0) HLsetup calls ACSetFuncTblBaseIndex(HLid, 1) Now when LCPmain(run phase) wants to call a state action for case HD, DoTransition for the AC of HD can use p = (*FunctTblBase[ACGetFuncTblBaseIndex(HDid)])[STcurr->ActFunc] instead of p= FunctTbl[STcurr->ActFunc] in lcp04s below. This has the advantage of decoupling the SM include files from each other as they were in Team1, but it still does not eliminates their sensitivity to the order in which StateModelCreateCompletely() is called for each SM in one AC. If this order changes, STcurr->ActFunc index will not be invariant although is still unique over multiple SM's within one AC - it is zero-based for the AC for each SM. To make this invariant, we need a separate FunctTBl for each SM. So it looks like three levels are needed: a local FunctTblBase[] for each AC, and a global array of pointers to these arrays indexed by AC number. [olc gets and sets fields of table rows using access functions like 'Get()'; olc3common.h generates these for every field of every table type. They do not require the pre-condition 'assert(EIcurr->EIid == EIid)', because they call pr_find(), which searches for a matching pkey, before ProcessOneEvent() calls DoTransition(TRcurr->STid2, AIid, EIid). (Chgen13 improved pr_find as discussed later below.)] Here is the lcp04s version of DoTransition for function pointer access: ----------- /* Alfredo Lopez */ extern void (*FunctTbl[]); /* in HMfunctbl.h - RJL051013 */ . . . static FUNCTION3( void DoTransition, hcg_key, STid, hcg_key, AIid, hcg_key, EIid ) { void (*p)(hcg_key); /* ** First, put the active instance into the correct state */ ActiveInstanceSetState(AIid, StateGetName(STid)); /* ** And call the action routine */ /* Alfredo Lopez */ p= FunctTbl[STcurr->ActFunc]; (*p)(EIid); } --------------- The Set and Get routines call pr_find to get an XX-table row-object's address XXcurr from its pkey XXid: pr_find(XX, XXid, pkvalue) can avoid a search loop if (XXcurr->XXid == pkvalue). This is an important optimization for pr_util's pr_find macro. It was not done in chgenv12 used by lcp04s, but it is done in chgen13: E.g. at $RBGB/../bde/pr_util/94sbde_schema.h:618-619: -------- #define pr_find(tbl,pkey,value) \ if (key_compare(&tbl##curr->pkey,&value)!=0) /* else prior value matches */\ ...// enter the search loop*/ -------- The Set and Get routines are generated for each table type prefix and attribute name suffix by macros generated semi-automatically in lcp/olc/olc3common.h: ----------------- /* ** The GETROUTINE macro generates a routine to get the value of a field ** in an object. The macro takes four arguments. ** ** return_type - the return type of the generated function ** object_name - the name of the object containing the field ** object_abbrev - the object's abbreviation ** field - the field whose value you wish ** ** When using this macro, there must be no spaces after the before the field ** argument. With old style token pasting the space gets pass on resulting ** in Get/_*_*_/ State not Get/_*_*_/State (ignore the _, they are there to ** avoid nested comments ** ** GETROUTINE(const char *,ActiveInstance,AI,State) produces ** ** FUNCTION1( ** const char * ActiveInstanceGetState, ** hcg_key, AIid ** ) ** { ** pr_find(AI, AIid, AIid); ** if (AIcurr == 0) { ** return (const char *)0; ** } else { ** return (const char *)(AIcurr->State); ** } ** } ** */ ------------------ In chgenv13, a new file called pr_accessors.c automatically generates similar access function definitions in macro bodies following the lead of olc/olc3common.h, For example, in $RBGB/pr_util/pr_accessors.c: -------------- #define SETROUTINE(field_type,object_name,object_abbrev,field,value_type,value) \ FUNCTION2( \ void P3(object_abbrev,Set,field), \ hcg_key, P(object_abbrev,id), \ value_type, value \ ) \ { \ pr_find(object_abbrev, P(object_abbrev,id), P(object_abbrev,id));\ if( P(object_abbrev,curr) != NULL) \ P(object_abbrev,curr)->field = (field_type)value; \ } [SETROUTINE2 does strcpy(P(object_abbrev,curr)->field, (field_type)value); (client must ensure value does not overflow field length.)] #define GETROUTINE(return_type,object_name,object_abbrev,field) \ FUNCTION1( \ return_type P3(object_abbrev,Get,field), \ hcg_key, P(object_abbrev,id) \ ) \ { /* pr_find alters ??curr: */ \ pr_find(object_abbrev, P(object_abbrev,id), P(object_abbrev,id)); \ if (P(object_abbrev,curr) == 0) { \ return (return_type) 0; \ } else { \ return (return_type)(P(object_abbrev,curr)->field); \ } \ } [COPYROUTINE(object_name, object_abbrev, field) generates function void P(object_abbrev,Copy,field) (hcg_key P(object_abbrev,id), char * CopyDst) (client must avoid string oveflow at CopyDst.).] -------------- After these definitions, pr_accessors.c merely calls the Set or Get macro 164 times to define the corresponding Set/Get functions: [All but 4 are suppressed since none of them are in use. There are still some TBDs to check for NUll pointer values. These might require an extra default_value argument.] ------------ SETROUTINE(hcg_key,fontoption,FO,FOid,hcg_key,value); GETROUTINE(hcg_key,fontoption,FO,FOid); SETROUTINE2(char*,fontoption,FO,xfont,char*,value); GETROUTINE(char*,fontoption,FO,xfont); #if 0 SETROUTINE2(char*,fontoption,FO,psfontname,char*,value) GETROUTINE(char*,fontoption,FO,psfontname) . . . SETROUTINE2(char*,graphtxt,GX,grphcaption,char*,value) GETROUTINE(char*,graphtxt,GX,grphcaption) /* end pr_accessors.c */ #endif