The edit phases of all modules are quite similar, as far as the parameters input is concerned. Generally, the edit phase will allocate the module's data block and then read the parameters in. The parameter groups are input in the sequence they appear in the job file: first the parameters of the global group, then its repeat groups, if there are any; then the lists in the sequence they appear in the job. The job parser offers options for calling the lists by name (and within every group of lists with the same namein the sequence of their appearance) or sequentially looking through all the lists as they are provided in the job flow description.
Within a parameter group, the parameters can be requested in a random order, and any number of times. Only one group of parameters is visible at any moment; switching between the lists and repeat groups makes the previously read parameters invisible for the functions that deliver their values to the Edit phase.
An important function of the Edit phase is the error control. All errors should be reported using the function SIA_err() (see the section Components below). This function provides three options of response of the monitor: error ignored (warning), the process phase of the job canceled (normal error), and the job terminated immediately after the Edit phase of the current module returns (fatal error).
The Edit phase returns to the monitor the "module type", specifying the style in which the Process phase will be called afterwards. These types are:
The return types above may be modified by adding a constant C_PLUS_PLUS_MODULE indicating that the module uses the full scope of tools provided by the C++ interface.
To approach programming in a systematic manner, we start with a simple template of the edit phase for our module ourmod. We assume that class OURMOD is defined in the file ourmod.h, as it was explained in the section Module Data Organization above. Module parameters are arranged into lists and repeat groups. Each such group of parameters can be accessed in absolutely identical ways. Several special functions allow switching between the lists and repeat groups. First, I will show how to switch between the lists, then-how to read parameters and how to access repeat groups.
Two types calls to parameter lists are usually employed. First, it is often necessary to read the lists (at least, some subset of lists) in the order they they are provided n the job description and regardless of the list names. By contrast, sometimes we need to scan all the lists of the same kind (having the same name) without looking at the other lists.
Let us consider the first of these cases and read all the lists in the order they are present. This task is achieved using the function PDF_next_list():
#include "sia_module.C.h" /* include all basic declarations */
#include "ourmod.h" /* include declarations of specific data structures used in this module */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
Edit phase of OURMOD
..........................................................................*/
int ourmod_editp_ ( OURMOD **O )
{
#define LIST_!_NAME 1 /* define meaningful constants for list IDs */
#define LIST_2_NAME 2
#define LIST_3_NAME 3 /* use any positive integers */
TABLE lists[] = /* associate the names of the lists with constants */
{
"list1", LIST_!_NAME, /* e.g., 'list1' with the integer */
"list2", LIST_2_NAME, /* constants defined above, such as */
"list3", LIST_3_NAME,
"". 0 /* this means the end of the table */
};
LIST *list; // pointer to the current parameter list object
while ( list = PDF_next_list() )
switch ( list->code(lists) )
{
case LIST_!_NAME : // we are in "list1", ready to read its parameters
break;
case LIST_2_NAME : // we are in "list2", ready to read its parameters
break;
default : // we are in a list other than "list1" or "list2"
}
}
The function LIST::code() above looks up the list's name in the table of tokens provided, and returns the corresponding token or a constant PARAM_ERROR, if not found.
In the following example, we are interested in all occurrences of a specific lists, e.g., named input and output:
while ( PDF_list("input") )
{
// read the parameters of list "input"
}
while ( PDF_list("output") )
{
// read the parameters of list "output"
}
At any point during parameter input, the function PDF_current_list() returns a pointer to the current parameter list. By calling PDF_reset_lists(), we can reset reading of all parameters to its initial state, the one corresponding to reading of the global list (the parameters immediately following the module call).
How to read parametersA couple of examples explain the typical use of the function. Suppose we have defined a field in the .mpar file called 'VALUE', of INTEGER format. Then the following operator reads the value put into the field by the user:
{
int i;
switch ( PDF_parameter_f ( "VALUE", INTEGER, &i ) )
{
case PARAM_ERROR :
SIA_err ( SIA_abort, "Expected integer value" );
break;
case PARAM_DEFAULT :
SIA_err ( SIA_ignore, "Will use default value of 1" );
i = 1;
break;
case PARAM_NORMAL : SIA_message("Got VALUE = %d", i );
}
}
This example also illustrates the use of error and information messages. A shorter (silent) version of the input used most commonly is:
if ( PDF_parameter_f ( "VALUE", INTEGER, &i ) ) != PARAM_NORMAL )
i = 1;
The same function works with all formats of input. One only needs to remember that in reading character strings, it is necessary to provide a sufficient buffer for the result, as in this example:
{ char s[STEP+1]]; PDF_parameter_f ( "STRING", CHARACTER, s ); }
In this example, the constant STEP is the tab step, (currently 8). This command reads a C8 field from the job file, leaving enough space to put the null symbol closing the character string. The use of fixed-length strings, however, is obsolete. I much better equivalent to the above is using the CHARSTR class:
{ CHARSTR s; s.param("STRING" ); }
To make job files more readable, it is advisable to try using meaningful symbolic names of various processing modes, switches, etc. To do this, it is necessary to compare the name read from a character parameter field with the entries of a list of possible values. For example, suppose we want to compare the word input by the user in the field called "MODE" against a list of values "RMS", "PEAK", and "CONST". It is done easily with the use of the function with a long name of PDF_look_up_string_parameter ():
{
#define PEAK 1 /* the definitions make listing clearer */
#define RMS 2 /* use any positive integers */
#define CONST 3
TABLE modes[] = /* names of the modes, as we want to see them */
{
"peak", PEAK,
"rms", RMS,
"const", CONST,
"", 0
};
int mode;
switch ( mode = PDF_look_up_string_parameter ( "MODE", modes ) )
{
case PARAM_ERROR :
SIA_err ( SIA_abort, "Unknown mode" );
break;
case PARAM_DEFAULT :
SIA_error ( SIA_ignore, "Will use the default mode: RMS" );
mode = RMS;
break;
default : SIA_message ( "The mode is %d", mode );
}
As we see, instead of PARAM_NORMAL, this function returns the number of the mode, as defined in the table modes.
The third common case of parameter input is the input of a "header entry or numerical value", when a C8 field can be interpreted as the name of a trace header entry, or as a numerical value. The values of such field are stored in objects of class HEADER_PARAM (see above), and they are obtained from the job file using its member functions read(), returning the same statuses, as the function described above:
{
#define def 3.14159265
HEADER_PARAM p;
switch ( p.read ( "PI", REAL, def ) )
{
case PARAM_DEFAULT :
SIA_err (SIA_ignore, "The field has been left blank" );
break;
case PARAM_NORMAL : SIA_message ( "Got Pi = %f", p.value() );
}
}
The third argument of HEADER_PARAM::read() function is the default value, used if the corresponding field in the job file is blank.
For any list pointed to by the pointer list (see the Edit phase template above), the number of repeat groups actually read from the job file can be obtained from the field list->groups_number. In any case (even for a global list) the pointer to it is returned by the function PDF_current_list (), and thus the expression PDF_current_list ()->groups_number also gives the number of repeat groups. To switch to the first and to the subsequent repeat groups, use PDF_next_repeat ():
while ( PDF_next_repeat() )
{
/**************************************************
read parameters of one repeat group, as above
***************************************************/
}
If it is necessary to re-read the repeat groups within one list from the beginning, you can restart reading using PDF_reset_repeat ().
Two of the typical operations used in the Edit phase are trace header access and definition. These functions are best perfomed using the AHEADER class. You can obtain a pointer to a trace header already defined using the function AHEADER::get (), and to define a new trace header using AHEADER::define():
{
AHEADER h_in, h_out;
h_in.get ( "NAME1", FROM_HERE );
h_out.define ( "NAME2", 16, CHARACTER, FROM_JOB );
}
FROM_HERE and FROM_JOB are integer constants defined to distinguish between the situations when we want to access a header with the name specified in the first parameter, or when we intend to obtain the name from the job file. In this example, the first operation finds a trace header called "NAME1", or NULL, if the header does not exist. The second operation defines a new header entry with the name obtained from the job file, from the field "NAME2" . The new header has a length of 16 characters, and type CHARACTER.
Several variants of the above functions are available. If you want to
make certain that the requested header entry is present in the trace headers,
use another form of h_in.get
:
h_in.get ( "NAME1", FROM_HERE, SIA_abort );
which calls the corresponding erro handler if the entry is missing. As in other error-handling functions (see section Error handling and messages to the user).
When defining a header of a numeric format, you can use the function SIA_entry_length()
returning the
standard length of the field required (although you can use a longer field, but it is
hardly necessary):
h_out = PDF_define_header ( "NAME", SIA_entry_length ( REAL ), REAL, FROM_JOB );