Commander includes the following components:
The quickest way to understand Commander is to try it from the VFP Command Window by running the following command:
? commandr( )
This will automatically initialize the default meta-file and evaluate the default sample function, which just displays a WAIT WINDOW message. Creation and maintenance of the Commander meta-file can be done via VFP's BROWSE command, and this is made easier by taking advantage of VFP's Browse preferences, e.g. via the BROWSE LAST option. Of course you can also populate the Commander meta-file programmatically, which is a major benefit of the design. (See the Saver application for an example of totally programmatic meta-file creation and use of the Commander facility.)
Try running commandr( ) against the sample pseudo-function definitions, which you can review in a BROWSE window. The current record determines the default functionid, when you pass no arguments to commandr( ). Go through the examples in sequence, to see the simplest ones first. Look in the Notes field for explanatory comments about each function. The meta-file records for functionid = 'demo07', for example, illustrates the factorial function. The example for functionid = 'demo07b' shows another way to implement the factorial function more cleanly and efficiently, using the m.cmdr_self global memory variable and direct calls to method cmdrm_dofunc.
To clean up after testing commandr( ) without closing out the current VFP session, use this command:
This will release the default Commander object and close the meta-file, which is useful during development and testing of the Commander facility itself, to avoid "class is in use" errors.
Both the Commander shell UDF, commandr( ), and the primary Commander method, CMDRM_DoFunc, support the same calling syntax, i.e. they take the same arguments and return the same result. From VFP command level you would use the UDF interface supported by commandr.prg/fxp/app, which also handles various initialization details. (If you are using the Commander class-level interfaces, you have explicit control over more flexible initialization options.) In either case, the following 10 optional arguments are supported:
Because the arguments are declared PARAMETERS (as opposed to LPARAMETERS) in method CMDRM_DoFunc, they behave as PRIVATE memory variables, scoped to be visible during the execution of metafile-based function steps. In addition to these arguments, a Commander function can employ any of the following global memory variables, declared as PRIVATE and initialized by CMDRM_DoFunc:
|cmdr_exit||forced termination flag (for convenient EXIT from the processing loop). Setting this flag to .T. will cause CMDRM_DoFunc to terminate without proceeding to the next step defined in the meta-file.|
|cmdr_funid||current functionid being processed, which may have been determined by default if no explicit cmdr_idarg was supplied.|
|cmdr_next||next sequence number to GOTO, if value is numeric. Executing a step that assigns a sequence number to this mvar overrides the normal sequential flow, instead jumping directly to the given seq # within the same functionid. Upon making such a jump, the value of m.cmdr_next is automatically reset to .F. If the given seq # is not found (for the current functionid), an error is produced. No special action is taken if m.cmdr_next is non-numeric, or if the end of the pseudo-function is encountered (i.e. if m.cmdr_next is assigned a value in the last step of the function definition). There is no check for possible infinite loops, so use this feature with care.|
|cmdr_nuarg||count of optional user-defined arguments (not including the initial cmdr_idarg). This is only useful when Commander's CMDRM_DoFunc method is called directly within an application, as opposed to calls originating from the shell UDF interface, commandr( ).|
|cmdr_retvl||function return value (defaults to .T.). By assigning a value to this variable, you can set any type of value to be returned as the result of the CMDRM_DoFunc. If an error is encountered, however, the result will always be .F., regardless of the value of m.cmdr_retvl. (Detailed error information is returned via the standard ErrHandler mechanism, so you can easily sort out error cases from other results.)|
|cmdr_self||current Commander object reference, i.e. a way of referring directly to the Commander class object that is running the present function evaluation.|
The Commander class is a modular, general purpose component that can be used in any VFP application to support dynamic, table-driven functions. The following custom properties and methods are attached to each Commander object (in additional to the standard error handling properties and error handling methods inherited from ErrHandler):
Commander uses a "meta-file" to store its representation of user-defined functions. This is simply an indexed VFP table of records that define one or more uniquely named pseudo-functions, each comprised of a sequence of steps. The Commander application includes a sample meta-file and an empty template meta-file, from which any number of additional Commander meta-files can be generated automatically (by logic in the Init event method). These files all share the following structural details:
|FUNCTIONID||Character||10||Id of the pseudo-function to which this record belongs. Each record defines a step in the function logic. Functionid + seq comprises a unique key for each record. Commander allows the length of this field to be altered, but it defaults to 10.|
|SEQ||Numeric||10||Sequence number for ordering of steps within each function definition (regardless of the physical record order). Gaps are allowed in the sequence of steps; only the order matters. This field forms the suffix of the unique key for each record. Commander allows the length of this field to be altered, but it defaults to 10.|
|CMDLINE||Memo||4||Arbitrary VFP command line for this step of the function logic. This will normally be invoked by &-macro expansion in Commander's CMDRM_DoFunc method. Commander also supports a special string assignment syntax, if the command line begins with "*=".|
|NOTES||Memo||4||Optional free-form notes pertaining to this step of the pseudo-function. (The Notes field can be removed altogether, if desired, because the program logic does not require it to be present.) Used for annotation of the sample meta-file.|
|MODIDATE||DateTime||8||Date and time of last modification to this record. Use this for manual maintenance of the meta-file. Also maintained automatically by Commander's CMDRM_AddFuncStep logic. (The name of this field is actually determined by macro variables, FNAM_DTMOD and FNAM_DTMNQ defined in include file mdacomm.h, in order to support alternate global naming conventions.)|
|DELETED||DELETED( )||(for the benefit of VFP's Rushmore query optimization)|
|FUNCSEQ||FUNCTIONID + STR(SEQ)||This is the most useful ordering, by function id, by sequence number. Logically used like a unique key, but uniqueness is not rigidly enforced.|
|FUNCTIONID||FUNCTIONID||(simple field index - for query optimization)|
|MODIDATE||MODIDATE||(simple field index - for query optimization)|
|SEQ||SEQ||(simple field index - for query optimization)|
The default interpretation of each step in a function definition, i.e. the CMDLINE field, is to treat it as a VFP command line, which is processed via VFP's &-macro expansion capability. While this is an extremely powerful feature (unique to Visual FoxPro), it has certain limitations. Commander also supports an alternate syntax in the CMDLINE field, for actions that reduce to assigning a literal string value to a name expression. This special syntax is more efficient than &-macro expansion, and it also avoids some VFP limitations and inconveniences when dealing with very long strings, or strings containing special characters (like quotes and newlines).
For example, the following CMDLINE value:
x = "here is a literal character string value"
Can be reformulated in Commander's special literal string assignment syntax as:
*=x=*here is a literal character string value
The "*=" prefix (defined as macro CMDM_DLAS1 in include file commandr.h) tells Commander that this is a special case of literal string assignment. The "=*" delimiter (defined as macro CMDM_DLAS2) marks the end of the name expression, in this case the variable x. Everything that follows this delimiter is taken to be the character string value to be assigned to x, without surrounding quotes or any concerns about special characters, up to VFP's maximum memo field length.
Apart from the Commander class library there is a separate, but related utility program for cloning metafile-based function definitions. This function has the following usage:
success = cmdrclon(fromid_arg, toid_arg, metafi_arg, outfil_arg)
with the following 2 - 4 arguments: