• Antelope Release 5.9 Mac OS X 10.13.2 2019-05-01

 

NAME

BUConfigure - BRTT configuration utility

SYNOPSIS


-lbrttutil

#include "BUConfigure.h"

DESCRIPTION

BUConfigure objects implement complex configuration of program parameter structures from either Antelope parameter file objects or from variable argument lists. This utility is loosely based on the Tk extension utility that allows configuration to be accomplished by key-value pairs in tcl/perl/python extensions.

The basic concept with this utility is for an application programmer to define a parameter structure that gets filled in from either parameter file input or from variable argument list key-value pairs using the configure methods (see below). The parameter structure can be as complex as needed. The BUConfigure class is already set up to parse out simple scalar int, double and char * parameters. The application programmer can extend this for other more complex and custom parameter parsing through callback procedures. Care has been taken to make it easy to subclass BUConfigure to extend the parsing capabilities. Custom parsers can even be used to initiate program actions.

The basic configuration "switchboard" is a structure, BU_ConfigureSpec, that the application programmer must provide that relates locations in the application parameter data structure to key names and data types.


typedef struct BU_ConfigureSpec {
    const char *type;
    const char *name;
    const char *defValue;
    int offset;
    int specFlags;
    struct BU_CustomOption *customPtr;
} BU_ConfigureSpec;

The type element is a data type string. BUConfigure comes with a set of predefined data types it can parse, "boolean", for boolean parameters (yes, no, on, off, etc.), "int", for 32-bit integer data, "double", for double foating data, "string", for string data that will be allocated and copied to char * parameter pointers, and I"ptr" for generic pointers. The name element is a parameter name string and is arbitrary. Parameter name strings should be unique across a single instance of BUConfigure and any subclasses. The defValue element is a default value string that will be parsed when the BUConfigure object method setConfigureSpecs is called. The offset element is a byte offset into the application program parameter data structure corresponding to the parameter value. The specFlags element are processing flags that are defined below. The customPtr element points to an application programmer defined callback procedure for doing custom processing of this parameter, as described below.

The application programmer must first specify a set of these structures through a call to the setConfigureSpecs method before processing any configuration parameter files or specifications through variable argument lists using the configure methods.

CONSTRUCTORS

BUConfigure();

METHODS

  • int addTypeParser (const char *type_name, BU_TypeParserProc proc);
    By default, BUConfigure objects already know how to parse "boolean", "int", "double" and "string" parameters. This method allows a new data type to be defined, along with a function callback to implement the parsing. The new data type string is specified as type_name and must be unique relative to any previously defined data type strings. The callback is specified by proc which is a procedure pointer to a function that will be called as follows.
    
    typedef int (BU_TypeParserProc) (BU_ConfigureSpec *spec, char *value, char *paramRec);
    
    
  • Where spec points to a BU_ConfigureSpec structure that defines the parameter, value is the value as defined by defValue in the BU_ConfigureSpec structure for default configuration, or it comes from either input parameter file objects or variable argument lists using the configure methods and paramRec points to the application parameter data structure. The method must return one of BU_CONFIGURE_OK, if the configuration was successful, BU_CONFIGURE_ERROR, if there was an error during configuration, of BU_CONFIGURE_UNRECOGNIZED_TYPE if the data type is unrecognized.
  • This method can be called multiple times to define a set of new data types. This method must be called prior to calling the setConfigureSpecs method for each new data type that needs to be defined.
  • int setConfigureSpecs (BU_ConfigureSpec *configSpecs, char *paramRec, const char *class_name);
    This method must be called prior to parsing parameters with the configure methods. A list of BU_ConfigureSpec structures define the various parameters to be parsed. This is typically defined as a static array of BU_ConfigureSpec structures by the application programmer with the last element in the array having type equal to "end", which signals the end of processing for the array of structures (the other fields in this end structure are ignored). The paramRec argument is the pointer to the associated application parameter structure. The class_name string as an optional string (NULL to ignore) that can be used to define the class corresponding to these parameter definitions and is typically used to determine which class or subclass made the definitions. This method can be called multiple times to add to the parameter list.
  • As discussed previously, the BU_ConfigureSpec structures contain data type, parameter name and location information. The specFlags elements are bitmasks that control the processing. The BU_CONFIGURE_NULL_OK bit, if set, means that it is ok to configure a parameter with a NULL or "" value, otherwise attempts to set parameters with NULL values will generate an error. The BU_CONFIGURE_DONT_SET_DEFAULT bit, if set, means do not attempt to set a default value for that parameter when setConfigureSpecs is called. The BU_CONFIGURE_OPTION_SET bit is cleared automatically before the parsers are called and should be set by the parsers if a new value was specified for the parameter.
  • The customPtr elements should be set by the application programmer to pointers to BU_CustomOption structures whenever the application programmer wants to use custom parsers for particular parameters. In these cases the type strings should always be "custom". The BU_CustomOption structure is defined as follows.
    
    typedef int (BU_OptionParseProc) (BU_ConfigureSpec *spec, BU_ClientData clientData,
                                              char *value, char *paramRec, int offset);
    typedef char *(BU_OptionPrintProc) (BU_ConfigureSpec *spec, BU_ClientData clientData,
                                              char *paramRec, int offset);
    
    typedef struct BU_CustomOption {
        BU_OptionParseProc *parseProc;
        BU_OptionPrintProc *printProc;
        BU_ClientData clientData;
    } BU_CustomOption;
    
    
  • Callback functions are defined with the parseProc and printProc function pointers. These functions are then called using the arguments specified by the BU_OptionParseProc and BU_OptionPrintProc typedefs. Application program client data can be specified with clientData which is passed to the callbacks. The paramRec pointer in the callback arguments is the parameter data structure pointer defined in the call to setConfigureSpecs and offset is a byte offset to the location of the particular parameter relative to paramRec. The offset element can be computed with the helper macro BU_Offset (paramRec, name). The value is a pointer as defined by defValue in the BU_ConfigureSpec structure for default configuration, or it comes from either input parameter file objects or variable argument lists using the configure methods. The printProc callback is intended to provide a mechanism for returning the current parameter value in a form auitable for printing but is currently not used and can be set to NULL.
  • int configure (Pf *params);
    This parses parameters from an Antelope parameter file object specified by params.
  • int configure (...);
    This parses parameters from a variable argument list composed entirely of character string key-value pairs with a single NULL terminating argument. Key values can be specified either as char * strings or const char * strings. Currently values must be pointers, usually char * or const char * pointers, although they can be pointers to other objects when using custom parsers.

RETURN VALUES

All methods return integer values that indicate return status. Generally BU_CONFIGURE_OK is returned if the method executed successfully or BU_CONFIGURE_ERROR if there was an error during execution in which case error messages are left on the error message log. The configure methods will return BU_CONFIGURE_UNDEFINED_PARAMETERS if parameters have been specified that have not been defined with setConfigureSpecs and error messaages will be left on the error message log.

SUBCLASSING

Subclasses can be made from the BUConfigure nested as deep as necessary. Each subclass must define its own parameters with a call to setConfigureSpecs. Care must be taken in subclasses to avoid using parameter names that have been used by parent classes. All of the parent class parameters will be parsed with a single call to configure in the subclass. Subclasses may also define more expanded parameter parsing by either calling addTypeParser or by defining custom parse callbacks.

EXAMPLE


#include "BUConfigure.h"

/* define main class parameter data structure */

typedef struct MyClassStruct {
    int x, y, spc_w, spc_h, wtran;
    double latr, lonr, opacity;
} MyClassStruct;

/* prototypes for main class custom parsers */

static int myclass_parse_tran (BU_ConfigureSpec *spec, BU_ClientData clientData,
        char *value, char *paramRec, int offset);

static char *myclass_print_tran (BU_ConfigureSpec *spec, BU_ClientData clientData,
        char *paramRec, int offset);

static BU_CustomOption tranOption = {myclass_parse_tran,
    myclass_print_tran, (BU_ClientData) NULL
};

/* define configure specs for main class */

static BU_ConfigureSpec configureSpecs[] = {
    {"int",     "x", "0", BU_Offset (MyClassStruct, x), 0, NULL},
    {"int",     "y", "0", BU_Offset (MyClassStruct, y), 0, NULL},
    {"int",     "width", "0", BU_Offset (MyClassStruct, spc_w), 0, NULL},
    {"int",     "height", "0", BU_Offset (MyClassStruct, spc_h), 0, NULL},
    {"custom",  "wtran", "none", BU_Offset (MyClassStruct, wtran), 0, &tranOption},
    {"double",  "latr", "0.0", BU_Offset (MyClassStruct, latr), 0, NULL},
    {"double",  "lonr", "0.0", BU_Offset (MyClassStruct, lonr), 0, NULL},
    {"double",  "opacity", "1.0", BU_Offset (MyClassStruct, opacity), 0, NULL},
    {"end",     NULL, NULL, 0, 0, NULL}
};

/* custom parsers for main class */

static int
myclass_parse_tran (BU_ConfigureSpec *spec, BU_ClientData clientData,
        char *value, char *paramRec, int offset)

{
    int tran_old, tran;

    memcpy (&tran_old, paramRec+offset, sizeof(int));
    if (!strcmp(value, "none")) {
        tran = 0;
    } else if (!strcmp(value, "edp")) {
        tran = 1;
    } else if (!strcmp(value, "merc")) {
        tran = 2;
    } else if (!strcmp(value, "geog")) {
        tran = 3;
    } else {
        bu_register_error(0, "myclass_parse_tran: illegal value for tran: '%s'\n", value);
        return (-1);
    }
    memcpy (paramRec+offset, &tran, sizeof(int));

    if (tran != tran_old) spec->specFlags |= BU_CONFIGURE_OPTION_SET;

    return (0);
}

static char *
myclass_print_tran (BU_ConfigureSpec *spec, BU_ClientData clientData,
        char *paramRec, int offset)

{
    int tran;
    char string[8];

    memcpy (&tran, paramRec+offset, sizeof(int));
    switch (tran) {
    case 0:
        strcpy(string, "none");
        break;
    case 1:
        strcpy(string, "edp");
        break;
    case 2:
        strcpy(string, "merc");
        break;
    case 3:
        strcpy(string, "geog");
        break;
    default:
        strcpy(string, "NULL");
        break;
    }

    return(strdup(string));
}

/* main class subclassed from BUConfigure */

class MyClass : public BUConfigure {
public:
    MyClass () {
        memset (&(this->myclass), 0, sizeof(MyClassStruct));
        this->setConfigureSpecs (configureSpecs, (char *) &(this->myclass), "MyClass");
    };

    MyClassStruct myclass; /* main class parameter structure */
};

/* define subclass parameter data structure */

typedef struct MySubClassStruct {
    int ymode;
    int preserve_yscale;
    double constant_aspect;
    double spc_ytop;
    char *fill;
} MySubClassStruct;

/* prototypes for subclass custom parsers */

static int mysubclass_parse_ybb (BU_ConfigureSpec *spec, BU_ClientData clientData,
        char *value, char *paramRec, int offset);

static char *mysubclass_print_ybb (BU_ConfigureSpec *spec, BU_ClientData clientData,
        char *paramRec, int offset);

static BU_CustomOption ybbOption = {mysubclass_parse_ybb,
    mysubclass_print_ybb, (BU_ClientData) NULL
};

/* define configure specs for subclass - note new "color" type */

static BU_ConfigureSpec configuresubSpecs[] = {
    {"custom",     "ytop", NULL, BU_Offset (MySubClassStruct, spc_ytop), BU_CONFIGURE_NULL_OK, &ybbOption},
    {"boolean",     "preserve_yscale", "0", BU_Offset (MySubClassStruct, preserve_yscale), 0, NULL},
    {"double",     "constant_aspect", "1.5", BU_Offset (MySubClassStruct, constant_aspect), 0, NULL},
    {"color",     "fill", "black", BU_Offset (MySubClassStruct, fill), BU_CONFIGURE_NULL_OK, NULL},
    {"end",     NULL, NULL, 0, 0, NULL}
};

/* custom parsers for subclass */

static int
mysubclass_parse_ybb (BU_ConfigureSpec *spec, BU_ClientData clientData,
        char *value, char *paramRec, int offset)

{
    MySubClassStruct *sc = (MySubClassStruct *) paramRec;
    double bb_old, bb;

    memcpy (&bb_old, paramRec+offset, sizeof(double));

    if (value == NULL || *value == '\0') {
        bb = 1.e30;
    } else {
        if (sc->ymode == 1) {
            bb = str2epoch (value);
        } else {
            bb = atof(value);
        }
    }

    memcpy (paramRec+offset, &bb, sizeof(double));

    if (bb != bb_old) spec->specFlags |= BU_CONFIGURE_OPTION_SET;

    return (0);
}

static char *
mysubclass_print_ybb (BU_ConfigureSpec *spec, BU_ClientData clientData,
        char *paramRec, int offset)

{
    MySubClassStruct *sc = (MySubClassStruct *) paramRec;
    double bb;
    char string[32];

    memcpy (&bb, paramRec+offset, sizeof(double));
    sprintf (string, "%.15e", bb);

    return(strdup(string));
}

/* custom new data type parser for "color" data types */

static int
MyTypeParserProc (BU_ConfigureSpec *spec, char *value, char *paramRec)

{
    char *ptr;
    char **str_ptr;
    int ret;

    if (!strcmp(spec->type, "color")) {
        if (!(spec->specFlags & BU_CONFIGURE_NULL_OK) && value == NULL) {
            bu_register_error (0, "BU_ParseConfigValue: Cannot set NULL value if not BU_CONFIGURE_NULL_OK for %s.\n", spec->name);
            return (BU_CONFIGURE_ERROR);
        }
        str_ptr = (char **) (paramRec+spec->offset);
        ptr = NULL;
        if (value) ptr = strdup(value);
        if (BU_Strcmp(*str_ptr, ptr))    spec->specFlags |= BU_CONFIGURE_OPTION_SET;
        if (*str_ptr) free (*str_ptr);
        *str_ptr = ptr;
        return (BU_CONFIGURE_OK);
    }

    return (BU_CONFIGURE_UNRECOGNIZED_TYPE);
}

/* subclass subclassed from main class which inherits BUConfigure */

class MySubClass : public MyClass {
public:
    MySubClass () {
        memset (&(this->mysubclass), 0, sizeof(MySubClassStruct));
        this->addTypeParser ("color",    MyTypeParserProc); /* add new data type parser */
        this->setConfigureSpecs (configuresubSpecs, (char *) &(this->mysubclass), "MySubClass");
    };

    MySubClassStruct mysubclass; /* subclass parameter structure */
};

int
main (int argc, char **argv)

{
    MyClass *myclass = new MyClass ();

    /* configure main class */

    int ret = myclass->configure (
        "x", "0",
        "y", "1",
        "width", "100",
        "height", "200",
        "wtran", "edp",
        "opacity", "0.5",
        NULL
    );
    if (ret != BU_CONFIGURE_OK) {
        bu_complain (0, "myclass->configure() error.\n");
    }

    MySubClass *mysubclass = new MySubClass ();

    /* configure subclass - inherits parameters from main class as well */

    ret = mysubclass->configure (
        "x", "1",
        "y", "2",
        "width", "300",
        "height", "400",
        "wtran", "merc",
        "opacity", "2.5",
        "ytop", "-500.0",
        "preserve_yscale", "yes",
        "constant_aspect", "0.5",
        "fill", "blue",
        NULL
    );
    if (ret != BU_CONFIGURE_OK) {
        bu_complain (0, "myclass->configure() error.\n");
    }

    exit (0);
}

AUTHOR

Danny Harvey, BRTT
Printer icon