How to extend the functionality of MITIE

MITIE was written with two main features in mind: speed and extendability. On this page we will describe how it is possible to write your own network processes, link selectors, node selectors and link dimensioners. The way to extend functionality is to create a class which inherits from one of the following base classes, depending on the new module's intention:

Implementing a Network Process

To create a new network process you must create a class that inherits from jmitie::CjNetworkProcess and overrides the following methods:

jmitie::CjNetworkProcess::CjNetworkProcess(std::string args, jmitie::CjNetworkProcess::ctor_va_t va)

This is the main constructor and is used to create instances of the module for use on the network layer. args is the string provided as command line parameters, e.g. '0:grow{D,const=1}' and va is a struct containing a number of objects which describe the context in which this module will work. ctor_va_t is typedef'ed to jmitie::CjNetworkProcess_ctor_va_t which contains pointers and references to such objects as the topology layer which this process is to be associated with, a random number generator which is to be used as required by the constructor, and references to factories which create sub-modules (instances of link selectors, dimensioners and so on).

static std::string jmitie::CjNetworkProcess::getName_static()

This is a simple method that returns the freeform name of this module. See Module Naming for details.

static std::string jmitie::CjNetworkProcess::getProperty_static(const std::string &)

This is a simple method that returns the property value of the specified property key, or an empty string if the property key is not found. There are no restrictions on property naming, however there are some properties that all modules must provide values for, as described in Module properties.

std::string jmitie::CjNetworkProcess::getName()

This is a non-static version of jmitie::CjNetworkProcess::getName_static so the name can be accessed via a base-class pointer to an object. The body of this method is typically just { return getName_static(); }

std::string jmitie::CjNetworkProcess::getProperty(const std::string &)

This is a non-static version of jmitie::CjNetworkProcess::getProperty_static so the properties can be accessed via a base-class pointer to an object. The body of this method is typically just { return getProperty_static(prop_string); }

void jmitie::CjNetworkProcess::performAction( const jmitie::CjNetworkProcess::action_va_t & opts  )

This is the core of the new network process functionality. The context, i.e. the network layer which this process operates on, was already specified to the constructor and probably stored in this object's instance, and this method is called by the main MITIE executable once in every epoch for which the object reports not to be expired (via jmitie::CjNetworkProcess::expiredPolicy) and active (via jmitie::CjNetworkProcess::activeEpoch). By calling methods of the the network layer object (jmitie::CjNLTopology) specified to the constructor the process can then manipulate the layer or return statistics as it pleases.



Additionally, the following two methods could be over-ridden if the method by which active processes are chosen were to be changed.

bool jmitie::CjNetworkProcess::activeEpoch (const unsigned int t)

This should return true if this process is active in epoch t and should be performed, and false otherwise (see performAction(..) above).

bool jmitie::CjNetworkProcess::expiredPolicy (const unsigned int t)

This should return true if this process is expired, that is, its lifetime was a period before epoch t (see performAction(..) above). The process doesn't have to be active to be unexpired, but an expired process will never be active.

Network Process Example: linkNodes

In this section we describe how the linkNodes network process was created to give the reader an idea of how to create a new network process. The full source of linkNodes can be found in CjNP_linkNodes.cc and CjNP_linkNodes.hh and is decomposed below:

Header

class CjNP_linkNodes : public CjNetworkProcess {

private:
ctor_va_t::CjDimFactory_t::Visitor_shared_ptr m_link_dimensioner;
ctor_va_t::CjNodeSelectorFactory_t::Visitor_shared_ptr m_dest_selector_a;
ctor_va_t::CjNodeSelectorFactory_t::Visitor_shared_ptr m_dest_selector_b;
bool m_dupe_ok;

The private member variables - these are: one instance of a link dimensioner that will be required to generate a link weight for the new link, and two node selectors, which will select our endpoints for the new link. There is also a bool to store whether we can return a duplicate link or not (i.e. whether we overwrite existing links in the network, or just add new ones). Remember also that a number of protected attributes are inherited from jmitie::CjNetworkProcess - these include the epoch timing variables and a pointer to m_top, which we can optionally use to store the pointer to the associated network layer.

public:
std::string getName() { return getName_static(); }
static std::string getName_static() { return "linkNodes"; }

The functions to return the free-form name of this process - this is the name by which it will be referred to in the command line and MITIE output, and not the C++ class name. Bear in mind the limitations regarding naming in section Module Naming.

std::string getProperty(const std::string & prop) { return getProperty_static(prop); }
static std::string getProperty_static(const std::string & opt) {
        if(opt=="desc") return "Add a link between two nodes, selecting each node with two node selectors.";
        if(opt=="usage") return getProperty_static("desc") + "\n" \
        "node_selector_name1\n"\
        "node_selector_name2\tThe names of the two nodes selectors to use to chose a node pair which to connect.\n"\
        "link_dimensioner_name\tThe name of the link dimensioner to use on the new link.\n"\
        "[dupe_ok]\tIf specified a node pairs which are already connected are included in the node-pair selection(only applies when adding links).\n";
        throw std::invalid_argument("CjNP_linkNodes::getProperty_static(" + opt + "): Unknown property requested.");
}

The functions to handle properties of the module. The minimum required properties (see Module properties) are implemented and we don't need any more. The string returned by a request for property "desc" is used by MITIE in the output of --desc_procs and --help_all and the string returned by a request for "usage" is also used by --help_all.

CjNP_linkNodes(const std::string & args, const ctor_va_t & var_arg );
virtual void performAction( const CjNetworkProcess::action_va_t & opts );

These are implemented below.

~CjNP_linkNodes() { }
};

There is no clean-up to be done - attributes are either not owned by this object (jmitie::CjNetworkProcess::m_top), PODs (m_dupe_ok) or automatically destroyed (the Visitor_shared_ptr type attributes) with their own custom deleters.

Implementation

The header above is mostly house-keeping - here comes the interesting stuff..

CjNP_linkNodes::CjNP_linkNodes(const std::string & args, const ctor_va_t & var_arg ):
        CjNetworkProcess(args, var_arg),
        m_dupe_ok(false)
        {
        std::list< optval_t > opts(parseOptions(m_cmd, true));
 JLOG(4,        std::cout << "Creating " << getName() << std::endl; )

        m_dupe_ok = extractValuelessOption("dupe_ok", opts, getName_static());

        // there should be three remaining options only - two node selectors and one dimensioner
        extractFactoryTypeOption(opts, var_arg.m_dim_fac, m_link_dimensioner, THROW_ON_MISSING, getName_static(), CjNetworkDimensioner::ctor_va_t( var_arg, false, getName_static() ) );
        extractFactoryTypeOption(opts, var_arg.m_nodsel_fac, m_dest_selector_a, THROW_ON_MISSING, getName_static(), CjNodeSelector::ctor_va_t( var_arg, true, false, getName_static() ) );
        extractFactoryTypeOption(opts, var_arg.m_nodsel_fac, m_dest_selector_b, THROW_ON_MISSING, getName_static(), CjNodeSelector::ctor_va_t( var_arg, true, false, getName_static() ) );

        if(!opts.empty()) throw Cj_OptionParseFail("Extraneous options in process " + getName_static() + ": " + listOptionNames( opts ));
}

We are presented with two arguments in the constructor - a free-form string in arg, which contains the configuration string from the MITIE command line, and var_arg which is an object containing the context of the network process.
If we want to make use of the optional functionality in jmitie::CjNetworkProcess, such as timing and free-form string parsing, we call it via its base-class constructor. That constructor populates m_top, the timing attributes (and therefore we haven't over-ridden jmitie::CjNetworkProcess::activeEpoch() or jmitie::CjNetworkProcess::expiredPolicy() ). We then use the static method jmitie::CjNetworkProcess::parseOptions() to parse the parameters in the curly braces of the free-form argument into a list of strings (one for each arguments). This is also optional, and a different layout in the free-form parameters could be used and parsed at this point.
The remainder of the code parses and interprets the arguments. To maintain a common format for the arguments between modules MITIE provides a number of functions that can do the argument parsing - as described in Free-Form argument parsing.
These functions search a list of strings for the arguments they are told to expect and extract them from the list.
extractValuelessOption(...), for example, searches for a valueless (i.e. has no =value suffix) string of the name specified, and returns a true or false depending on whether it is found in opts. If it is found then it is removed from the list.
extractFactoryTypeOption(...) looks at each entry in the opts list and tries to find it in the module factory that it is specified. If it is found, then the module is instantiated and the argument removed. If it is not found, then as the THROW_ON_MISSING flag specifies, an exception will be thrown. There is one call to extractFactoryTypeOption(...) for each of the node selectors and the link dimensioner.
The last line checks that there are no remaining arguments left - there shouldn't be, as all correctly parsed options are removed from the opts list.

With the parameters and configuration set we look at the the performAction(...) method that does the actual real work of the module.

void CjNP_linkNodes::performAction( const CjNetworkProcess::action_va_t & opts ) {
        unsigned int node_a, node_b;

node_a and node_b will store the two end-points we've chosen for the link.

        CjNodeSelector::select_arg_t sa( opts );
        if(!m_dest_selector_a->selectNode( sa, node_a )) throw CjNP_selectionFail("CjNP_linkNodes::performAction: Couldn't find a start node.");
        sa.m_start_node = node_a;
        if(!m_dest_selector_b->selectNode( sa, node_b )) throw CjNP_selectionFail("CjNP_linkNodes::performAction: Couldn't find a node to connect start node to.");

Here we create an object which will pass the context to the node selector and call the source node selector to get the source node. This node is then added to the context which is presented to the second node selector.

        CjNetworkDimensioner::calcw_va_t dva ( opts );
        CjNLTopology::WT wt1 = m_link_dimensioner->calcWeight( dva, node_a, node_b );
        CjNLTopology::WT wt2 = m_link_dimensioner->calcWeight( dva, node_b, node_a );

With the source and destination nodes known for the new link, we create a context and call the link dimensioner - twice - once for each direction.

        // check weight to see if already connected
        if( (!m_dupe_ok) && ((m_top->getWeight(node_a, node_b) != 0) || (m_top->getWeight(node_b, node_a) != 0) ) )
                        throw CjNP_selectionFail("CjNP_linkNodes::performAction: Selected nodes " + boost::lexical_cast<std::string>(node_a) + " and " + boost::lexical_cast<std::string>(node_b) + " are already connected.");

TODO - dupe_ok here and not in context to second call to selectnode???

        std::pair<CjNLTopology::WT, CjNLTopology::WT> prev_wt;
        prev_wt = m_top->setBiDirWeights( node_a, node_b, wt1, wt2 );
        assert(prev_wt.first==0);
        assert(prev_wt.second==0);
}

Finally we tell the topology to connect the nodes and assign the weights we found earlier. The returned value contains the previous link weights, which should be 0 as this is a new link.

Comments on linkNodes

The comments in this section are no longer valid, but remain for users of older version of MITIE. This may seem like a relatively simple example, but there are actually a number of limitations in linkNode, which are actually caused by the MITIE framework. The limitation is caused by the fact that the call to the source node selector has no concept of nodes that it should avoid when making its choice - the call to the destination node selector is told not to select the source node via the context. This could cause the situation that a randomly chosen source node has no suitable destination nodes and the return value of the destination selector causes a jmitie::CjNP_selectionFail to be thrown. What should happen, ideally, is that the source node is marked as invalid as a source choice, and the source selector should be called again. There is currently no support for this, although it is on the Future work list.

Implementing a Link Selector

The implementation of a new link selector is similar to the network process implementation except that jmitie::CjLinkSelector has to be derived from. Instances of a link selector are typically (I've never considered a scenario where they're not) sub-ordinate to a network process and as such they are created for, and possibly, by, a network process, rather than the main MITIE executable. Their arguments are passed from the network process.

// Construct a CjLinkSelector base by copying settings from the CjLinkSelector_ctor_va_t object
CjLinkSelector(const ctor_va_t & va):m_top(va.m_top),m_add_remove(va.m_add_remove),m_parent_name(va.m_parent_name) {}

The main constructor for CjLinkSelector only takes the context as an argument and not the free-form parameter string as the base class doesn't require any parameters - only the context.

// Returns the free-form name of this class (static version)
static std::string getName_static() { throw std::logic_error("CjLinkSelector::getName_static() called."); }
// Returns a property of this class (static version)
static std::string getProperty_static(const std::string &) { throw std::logic_error("CjLinkSelector::getProperty_static(..) called."); }
// Returns the free-form name of this class
virtual std::string getName() = 0;
// Returns a property of this class
virtual std::string getProperty(const std::string &) = 0;

The meaning and the implementation of the naming and property functions are identical to those in jmitie::CjNetworkProcess, and are also covered by Module Naming and Module properties rules.

// performs the link selection
virtual bool selectLink( select_arg_t &, unsigned int & from, unsigned int & to  ) = 0;

This is where most of the work is done. select_arg_t is typedef'ed to jmitie::CjLinkSelector_select_va_t and provides the context of the call to selectLink, and the selected node pair are returned in from and to.

virtual ~CjLinkSelector() {};

The destructor has nothing to do since it doesn't create or take ownership of any dynamically allocated resources.

// The topology that this link selector is associated with
const CjNLTopology * m_top;
// if false then this selector will be adding a link, removing otherwise
bool m_add_remove;
// The free-form name of the parent
std::string m_parent_name;
// The link selection wheel
// In most implementations of a link selector there will be the need for roulette wheel selection to choose the link.
// The number of entries in the wheel is not known in advance so a std::vector is a suitable container.
// Including this common functionality in the base class, and as a static minimises vector (re-)creation, re-sizing, and memory usage (because all link selectors will share the same wheel)
// m_wheel.clear() should be called before use because the initial state is unknown.
// NOTE: this breaks MT safeness
static std::vector<wheelVal_t> m_wheel;

There are a number of protected attributes in jmitie::CjLinkSelector which are only here because they are common to all link selectors, but not really needed by the base class - m_top because the selector has to be associated with a topology, m_add_remove to specify if this link selector will be used for addition or removal, m_parent_name to store the name of the parent network process (used only in message printing), and m_wheel. m_wheel is a vector of jmitie::wheelVal_t objects which can, optionally, be used by the link selector implementation in the roulette wheel selection process. It is included in the base class so it can be shared between all link selectors and minimise the number of memory allocations and re-allocations in the vector during the simulation. m_wheel doesn't have to be used - if it isn't then it won't take up much space (one vector per base class, with no data buffer allocated).

Implementing a Node Selector

Implementing a Node Selector is identical to the method of implementation of a Link Selector process, only the jmitie::CjNodeSelector base class must be overridden and the following methods must be implemented:

CjNodeSelector(const ctor_va_t & va):m_top(va.m_top),m_parent_name(va.m_parent_name),m_src_dest(va.m_src_dest),m_add_remove(va.m_add_remove) { }

The ctor_va_t type object (typedefed to jmitie::CjNodeSelector_ctor_va_t) referred to by va contains a number objects (or references to) and values that define the context within which this node selector is to operate. These are stored in protected member variables which are described below.

// Returns the free-form name of this class (static version)
static std::string getName_static() { throw std::logic_error("CjNodeSelector::getName_static() called."); }
// Returns a property of this class (static version)
static std::string getProperty_static(const std::string &) { throw std::logic_error("CjNodeSelector::getProperty_static(..) called."); }
// Returns the free-form name of this class
virtual std::string getName() const = 0;
// Returns a property of this class
virtual std::string getProperty(const std::string &) const = 0;

These operate identically to methods of the identical declaration in CjNetworkProcess and CjLinkSelector

// performs the node selection
virtual bool selectNode( select_arg_t &, unsigned int & node ) = 0;

This method performs the majority of the functionality of the node selector. The select_arg_t is typedef'ed to CjNodeSelector_select_va_t which contains context information, such as the RNG, the epoch number and some variables to specify which nodes should be excluded from the node selection. The node which is selected is store in the reference provided by the node argument. The method returns false if a node cound not be found and true otherwise.

virtual ~CjNodeSelector() {};

Nothing to be done here (at least in the base class) as we don't own any dynamic resources.

protected:
// The topology that this node selector is associated with
const CjNLTopology * m_top;
// The free-form name of the parent
std::string m_parent_name;
// if false this node selector will be selecting a source node, otherwise a destination node
bool m_src_dest;
// if false then this selector will be adding a link, removing otherwise
bool m_add_remove;

These variables store the configuration or context for this node selector - it was probably provided to the constructor. The most important of these is m_top which is the topology with which this node selector is to operate on, the name of the parent process (for debugging pruposes mostly), m_src_dest is set to false if this node selector will be selecting a source node, otherwise a destination node. m_add_remove is false when this selector will be adding a link, removing otherwise.

Implementing a Link Dimensioner

Again, like the network process and link and node dimensioners a base class, this time jmitie::CjNetworkDimensioner, should be extend to add functionality. The only class variables are m_top and m_parent_name, which are set and used as in the cases above, and m_redim which is true if this is a re-dimensioner, i.e. the link already exists and a new capacity or weight is to be generated for it, or, if false this is a capacity or weight for a newly added link. Browsing of CjND_constant.hh source is recommended to see the operation of the class.

Exporting the new module to MITIE

With the additional functionality implemented it must be presented to MITIE so it appears in the factories that MITIE uses to invoke the functionality.
To do this the new module can either by statically linked with MITIE - how to do this should be obvious by looking at mitie.cc TODO, or compiled into a DLL and loaded dynamically. MITIE attempts to load all .so files in the default DLL directory ( ./libs ) and searches for a number of C-linkage static functions which are used as sub-factories to create and destroy instances of the modules. The four functions which are required to be in the DLL are:

The functions are named according to the module type they produce, so a link selector sub-factory would be interfaced by: create_lselect, destory_lselect, getNames_lselect, getProperty_lselect, a node selector sub-factory by: create_nselect, destory_nselect, getNames_nselect, getProperty_nselect, and a link dimensioner sub-factory by: create_dim, destory_dim, getNames_dim, getProperty_dim.
The functions to export the module can be simple, supporting a single module, or more complicated, supporting multiple modules as in mitie_lib_template.hh . The arguments and return values should all be as below which is an example of a simple single module export.

#include "JLOG.hh"      // not strictly necessary but it defines the JLOG macro for compile time configurable logging 
// class MyNewNetProcess : public jmitie::CjNetworkProcess { ... } defined here

extern "C" {

// The exported object creation function
// Could throw std::runtime_error if the name requested is unknown, as well as anything the constructor of MyNewNetProcess could throw
jmitie::CjNetworkProcess * create_proc (const std::string & name, const std::string & args, const jmitie::CjNetworkProcess::ctor_va_t & var_arg) {
                if(name != MyNewNetProcess::getName_static()) throw std::runtime_error("Unkown type (" + name + ") requested.");
                else return new MyNewNetProcess ( args, var_arg );
        }

// The exported object destruction function
// This has to exist in this translation unit, rather than just a "delete" in mitie.cc because the delete for the module could be over-ridden - and mitie.cc wouldn't see it - it would just use the default delete.
void destroy_proc (jmitie::CjNetworkProcess * p) {
        JLOG(4, std::cout << "Deleting " << p->getName() << " at " << std::hex << p << "." << std::endl; )
        delete p;
        }

// The exported function that returns a list of names of classes available from this sub-factory
std::vector< std::string > getNames_proc () {
        std::vector< std::string  > output;
        output.push_back( MyNewNetProcess::getName_static() );
        return output;
}

// The exported function that performs a lookup of a given property for a given object name
// returns empty string if class name is not recognised
// could throw anything MyNewNetProcess::getProperty_static(..) does if the property name requested isn.t known.
std::string getProperty_proc (const std::string & name, const std::string & prop_name ) {
                if(name != MyNewNetProcess::getName_static()) return "";
                else return MyNewNetProcess::getProperty_static(prop_name);
}

Module Naming

Each module has a name, which is a free-form string, associated with it, by which it is referred to in configuration and textual output. It is recommended that the use of spaces, commas and curly brackets is avoided as this may be interpreted as a delimiter or other special meaning by the default argument parsing (see Free-Form argument parsing). The name can be any string but it must be unique among all the modules as this could lead to confusion in the specification of node and link selectors and dimensioners in network process configuration strings. Also, by default MITIE will not overwrite a previous module with a second module of the same name. It is suggested, although in no way required, that network process modules whose purpose is informational are suffixed by "Dump" - MITIE will then group them together when listing the available modules at the start of MITIE's output.

Module properties

All modules have a pair (one static, one non-static) of methods that return a string which is the value of a requested property name. The names supported can be anything and could be dynamic - which would allow them to be used for some kind of rudimentary inter-module communication, but there are two property names that must be supported: "usage" and "desc". A request for the "usage" property should return a short, one line overview of what the module does. A request for "desc" should return an in depth description of how the module can be used and what the free-form options are. There is no specific requirement on how to handle unknown keys, but it is recommended to throw a std::invalid_argument exception, and all modules in the MITIE core libraries will do so.

Free-Form argument parsing

Each module is passed a string on creation which can be used by the module's constructor to set parameters. The format of this string is totally up to the module, but there is a default format that the core MITIE module libraries use which can also be used by any user-defined modules.
The default layout of this string is:

start[+lifetime][@step][,prio]:proc_name{[option1][,option2..]}

The format of the timing specification before the proc_name has already been described in the section Process timing syntax.
The proc_name is the free-form name as described in section Module Naming.
The name must be followed by a clause surrounded by curly braces - even if just an empty {}
This clause is one or more comma separated option strings that will be passed to network process proc_name.
An option string can either be a simple string, or a option/value pair, delimited by a string: option=value
The meaning of the option is dependant on the module - it could be something like "overwrite" which specified to a module like adjLDump that it is ok to overwrite the output file, or in "D={exp=-1,dupe_ok}" which, depending on the context, can assume that it refers to the "D" module and passes "exp=-1,dupe_ok" as options to that.

Default format option handling

While a module implementor can use any format for the free-form string arguments they may also use the same functions that the core libraries use to parse their arguments - these are all defined in Cj_option_helper.cc and exist in the jmitie namespace:

typedef std::pair< std::string, std::string > optval_t;
std::list< optval_t > parseOptions (std::string optionString, bool allowDupes = false, bool requireCurlies = true )

The optval_t simply holds a key/value pair. The parseOptions function takes the outermost string enclosed in curly braces and returns the comma delimited options found inside.

Further, Cj_option_helper.hh defines the following parsing functions:

template <typename FactoryType, typename ContainerType> inline bool extractFactoryTypeOption (ContainerType & cont, FactoryType & factory, typename FactoryType::Visitor_shared_ptr & result, throwFlags_t flags, std::string parent_name, const typename FactoryType::VisitorArgumentType & va )

This template function takes the complexity out of searching for subordinate modules - for example when writing a new network process module that requires a link dimensioner the following code would go through the list of options trying to match each option with a module name in the factory specified. If it finds one it requests the creation of an instance of the module (passing it the free-form arguments) and then removes the entire option from the option list.

template <typename POD, typename ContainerType> inline bool extractPODValueOption(std::string optionName, ContainerType & cont, POD & result, std::string parent_name, POD * min_val = 0, throwFlags_t flags = (THROW_ON_DUPE | THROW_ON_BADVAL) )

This template function iterates through the list of option strings looking for optionName. When it is found it attempts to parse the value of the option to a type declared by POD. The function will throw exceptions in the case of some errors - see jmitie::throwFlags_t. If the option and value are successfully found and parsed then the option will be removed from the list.

template <typename ContainerType> inline bool extractStringOption(std::string optionName, ContainerType & cont, std::string & result, std::string parent_name, throwFlags_t flags = (THROW_ON_DUPE | THROW_ON_BADVAL) ) {

This template function iterates through the list of option strings and looks for optionName, if it is found the value of the option is returned in result and the option is removed from the list.

template <typename ContainerType> inline bool extractValuelessOption(std::string optionName, ContainerType & cont, std::string parent_name, throwFlags_t flags = (THROW_ON_DUPE | THROW_ON_BADVAL) ) {

This template function searches the option list for optionName and if it is found and doesn't have an option value will be removed and true is returned, otherwise if not found false is returned. If it is found, but has a value associated with it then the function will throw an exception if THROW_ON_BADVAL is or'ed into flags.

template <typename ContainerType, typename RESULT_TYPE> inline bool extractValuelessMultiOption( const std::map< std::string, RESULT_TYPE > & options, ContainerType & cont, RESULT_TYPE & result, const throwFlags_t flags = (THROW_ON_DUPE | THROW_ON_BADVAL) );

This template function searches the option list for one of the strings which appear as keys in the options map, and if one of them are found and they don't have an option, then the associated token (the value of the string found as key) is returned in result and the option is removed from the option list. This allows the implementor to extract one of a list of options which are mutually exclusive:

enum mat_preproc_t { NONE, ADJ, COMBLAP, NOSIGNLAP, NORMLAP };

std::map< std::string, mat_preproc_t > optmap;

optmap["adj"] = ADJ;
optmap["comb_lap"] = COMBLAP;
optmap["sign_lap"] = NOSIGNLAP;
optmap["norm_lap"] = NORMLAP;
extractValuelessMultiOption( optmap, opts, result, THROW_ON_DUPE | THROW_ON_BADVAL | THROW_ON_MISSING );

throwFlags_t

The helper functions above take a parameter of type jmitie::throwFlags_t which specifies the circumstances under which the function will throw a jmitie::Cj_OptionParseFail exception. See the jmitie::throwFlags_t documentation for the possible values. The values can be OR'ed when multiple reasons for throwing are required, for example passing a value of (THROW_ON_DUPE | THROW_ON_BADVAL) will cause the function to throw if the element being extracted appears more than once, or if it has an invalid value - no exception will occur if the option is simply missing.

 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator