Command Line Interface Definition¶
When not working with Options derived from OEOptions,
OEInterface class can still be used to create a command line
application. For such usage, OEInterface has a convenience
constructor that takes an interface definition and a command line to
parse. The constructor will configure the interface with the specified
definition, then parse the options from command line passed in through
argv
.
Listing 1: Getting started with OEInterface command line parsing
#include <openeye.h>
#include <oesystem.h>
using namespace std;
using namespace OESystem;
const char *InterfaceData =
"!PARAMETER -b\n"
" !TYPE bool\n"
" !BRIEF A boolean parameter\n"
"!END\n"
"!PARAMETER -i\n"
" !TYPE int\n"
" !BRIEF An integer parameter\n"
"!END\n"
"!PARAMETER -f\n"
" !TYPE float\n"
" !BRIEF A float parameter\n"
"!END\n"
"!PARAMETER -str\n"
" !TYPE string\n"
" !BRIEF A string parameter\n"
"!END\n";
int main(int argc, char** argv)
{
OEInterface itf(InterfaceData, argc, argv);
cout << "-b = " << itf.Get<bool>("-b") << endl;
cout << "-i = " << itf.Get<int>("-i") << endl;
cout << "-f = " << itf.Get<float>("-f") << endl;
cout << "-str = " << itf.Get<string>("-str") << endl;
return 0;
}
The values specified on the command line are then accessed on the
OEInterface object using a Get
method that
corresponds to the type of the parameter. The following is an example
of executing the program in Listing
1
:
prompt> ./GettingStartedWithOEInterface -b -i 12345 -f 0.5 -str foo
-b = 1
-i = 12345
-f = 0.5
-str = foo
Warning
The convenience constructor will call the system exit function forcing the program to exit in the following situations:
The interface definition is malformed
The user has requested help on any of the parameters, e.g.,
--help -str
The command line is malformed, i.e., missing or extra parameters
For these reasons the convenience constructor may not be suitable for use in a library setting, it is intended for standalone applications.
Parameter Value Access¶
The Get
methods of the OEInterface returns the
value of the specified parameter the user entered on the command line,
or in the case that the parameter was not specified by the user on the
command line the default value specified in the interface definition
is returned. If there is no default or user specified value the
default constructed object is returned (i.e., 0.0f for float
parameters and an empty string for string
parameters) and a
warning is issued. For example, the following demonstrates how running
the program in Listing 1
without any arguments will display a lot of warnings about
uninitialized values being used.
prompt> ./GettingStartedWithOEInterface
Warning: OEInterface::Get, requesting value of unset parameter -b
-b = 0
Warning: OEInterface::Get, requesting value of unset parameter -i
-i = 0
Warning: OEInterface::Get, requesting value of unset parameter -f
-f = 0
Warning: OEInterface::Get, requesting value of unset parameter -str
-str =
The warnings can be avoided by using the
OEInterface::Has
method to check if there is no
default value or user specified value. Listing
2
demonstrates how to check for the existence of
certain parameters.
Listing 2: Testing for parameter existence with the Has method
#include <openeye.h>
#include <oesystem.h>
using namespace OESystem;
const char *InterfaceData =
"!PARAMETER -b\n"
" !TYPE bool\n"
" !BRIEF A boolean parameter\n"
"!END\n"
"!PARAMETER -i\n"
" !TYPE int\n"
" !BRIEF An integer parameter\n"
"!END\n"
"!PARAMETER -f\n"
" !TYPE float\n"
" !BRIEF A float parameter\n"
"!END\n"
"!PARAMETER -str\n"
" !TYPE string\n"
" !BRIEF A string parameter\n"
"!END\n";
int main(int argc, char** argv)
{
OEInterface itf(InterfaceData, argc, argv);
if (itf.Has<bool>("-b"))
std::cout << "-b = " << itf.Get<bool>("-b") << std::endl;
if (itf.Has<int>("-i"))
std::cout << "-i = " << itf.Get<int>("-i") << std::endl;
if (itf.Has<float>("-f"))
std::cout << "-f = " << itf.Get<float>("-f") << std::endl;
if (itf.Has<std::string>("-str"))
std::cout << "-str = " << itf.Get<std::string>("-str") << std::endl;
return 0;
}
The following demonstrates the output of the program in Listing
2
. Notice that only variables present on the
command line will trigger OEInterface::Has
to
return true
. This is an important distinction when dealing with
boolean
parameters, OEInterface::Has
will not
return the value of the boolean
parameter, just the existence of
it.
prompt> ./UsingOEInterfaceHas
prompt> ./UsingOEInterfaceHas -b
-b = 1
prompt> ./UsingOEInterfaceHas -i 2
-i = 2
prompt> ./UsingOEInterfaceHas -f 0.5
-f = 0.5
prompt> ./UsingOEInterfaceHas -str foo
-str = foo
Default Values¶
Using OEInterface::Has
all over a program can
become quite clumsy. Therefore it is recommended to use !DEFAULT
in the parameter interface definition to specify the parameter’s
default value whenever a default value can be specified. When a
parameter has a default value OEInterface::Has
will always return true
. Listing 3
demonstrates how to set a default value for a parameter.
Listing 3: Setting a default value for a parameter
#include <openeye.h>
#include <oesystem.h>
using namespace OESystem;
const char *InterfaceData =
"!PARAMETER -b\n"
" !TYPE bool\n"
" !DEFAULT false\n"
" !BRIEF A boolean parameter\n"
"!END\n"
"!PARAMETER -i\n"
" !TYPE int\n"
" !DEFAULT 2\n"
" !BRIEF An integer parameter\n"
"!END\n"
"!PARAMETER -f\n"
" !TYPE float\n"
" !DEFAULT 0.5\n"
" !BRIEF A float parameter\n"
"!END\n"
"!PARAMETER -str\n"
" !TYPE string\n"
" !DEFAULT foo\n"
" !BRIEF A string parameter\n"
"!END\n";
int main(int argc, char** argv)
{
OEInterface itf(InterfaceData, argc, argv);
std::cout << "-b = " << itf.Get<bool>("-b") << std::endl;
std::cout << "-i = " << itf.Get<int>("-i") << std::endl;
std::cout << "-f = " << itf.Get<float>("-f") << std::endl;
std::cout << "-str = " << itf.Get<std::string>("-str") << std::endl;
return 0;
}
This allows the code to take a very simple structure that doesn’t have
to care whether the parameter was actually specified on the command
line. The following is what is printed by the program in
Listing 3
when no arguments are
given.
prompt> ./UsingOEInterfaceDefaults
-b = 0
-i = 2
-f = 0.5
-str = foo
Requiring Parameters¶
Programs usually have a bare minimum set of parameters that must be
specified. A parameter can be marked as required in the interface
definition by using !REQUIRED true
in the parameter
definition. Listing 4
demonstrates
making all the parameters used in the previous example required.
Listing 4: Requiring all parameters to be specified.
#include <openeye.h>
#include <oesystem.h>
using namespace OESystem;
const char *InterfaceData =
"!PARAMETER -b\n"
" !TYPE bool\n"
" !REQUIRED true\n"
" !BRIEF A boolean parameter\n"
"!END\n"
"!PARAMETER -i\n"
" !TYPE int\n"
" !REQUIRED true\n"
" !BRIEF An integer parameter\n"
"!END\n"
"!PARAMETER -f\n"
" !TYPE float\n"
" !REQUIRED true\n"
" !BRIEF A float parameter\n"
"!END\n"
"!PARAMETER -str\n"
" !TYPE string\n"
" !REQUIRED true\n"
" !BRIEF A string parameter\n"
"!END\n";
int main(int argc, char** argv)
{
OEInterface itf(InterfaceData, argc, argv);
std::cout << "-b = " << itf.Get<bool>("-b") << std::endl;
std::cout << "-i = " << itf.Get<int>("-i") << std::endl;
std::cout << "-f = " << itf.Get<float>("-f") << std::endl;
std::cout << "-str = " << itf.Get<std::string>("-str") << std::endl;
return 0;
}
If any required parameter is missing from the command line
during the command line parsing stage an appropriate error message
will be thrown. The following demonstrates the error messages thrown
by the program in Listing 4
:
prompt> ./UsingOEInterfaceRequired
No arguments specified on the command line
Required parameters:
-b : An boolean parameter
-i : An integer parameter
-f : A float parameter
-str : A string parameter
For more help type:
./UsingOEInterfaceRequired --help
prompt> ./UsingOEInterfaceRequired -b
Fatal: Missing required parameter -i
Legal and Illegal Values and Ranges¶
Some parameter types can have legal/illegal ranges and values. These
are set with the !LEGAL_VALUE
, !ILLEGAL_VALUE
,
!LEGAL_RANGE
, and !ILLEGAL_RANGE
fields. The following are the
restrictions for certain parameter types:
-
bool
Boolean parameters cannot have legal/illegal ranges and values.
-
int and float
These parameters can have legal/illegal ranges and values. Checks for illegal ranges and values are performed first and if an invalid range or value is detected an error is returned. Then legal ranges and values are checked, and if the parameter setting matches or there are no legal ranges or values it passes.
inf
and-inf
can be used as range limits, along with regular numerical values.
-
string
String parameters can have legal/illegal values, but not ranges. Checks for illegal values are performed first and if an invalid value is detected an error is returned. Then legal values are checked, and if the parameter setting matches it passes. Legal/illegal values for the string type support up to 2
*
wildcards for pattern matches. This is commonly used to force a file extension, e.g.,!LEGAL_VALUE *.txt
.
Parameter Lists¶
Sometimes it is useful to allow multiple values for a given
parameter. The !LIST
field is used to specify that a parameter can
have multiple values separated by spaces on the command line. The
OEInterface::GetList
method should then be used to
get an iterator over the parameter’s values. Listing
5
demonstrates how to specify that a parameter
is a list of values and then how to access it with the
OEInterface::GetList
method.
Listing 5: Specifying that a parameter is a list of values
#include <openeye.h>
#include <oesystem.h>
using namespace OESystem;
const char *InterfaceData =
"!PARAMETER -strs\n"
" !TYPE string\n"
" !LIST true\n"
" !BRIEF Some string parameters\n"
"!END\n";
int main(int argc, char** argv)
{
OEInterface itf(InterfaceData, argc, argv);
std::cout << "-strs = ";
for (OEIter<const std::string> p = itf.GetList<std::string>("-strs"); p; ++p)
{
std::cout << *p << std::endl
<< " ";
}
std::cout << std::endl;
return 0;
}
The command line parser will then assign elements as values based upon
their position in the argv
that was passed to the program. This
allows the user to pass values that have spaces in them by simply
quoting the relevant section. The following demonstrates the behavior
of the program in Listing 5
:
prompt> ./UsingOEInterfaceList -strs foo bar blah
-strs = foo
bar
blah
prompt> ./UsingOEInterfaceList -strs "foo bar" blah
-strs = foo bar
blah
Positional Parameters¶
For commonly used parameters it is often useful to allow them to be
specified by their position on the command line. The !KEYLESS
record is used to specify that a parameter can be assigned from a
positional value on the command line. Listing
6
demonstrates how to specify input and
output file name parameters that can be specified from their position
on the command line.
Listing 6: Specifying keyless input and output file parameters
#include <openeye.h>
#include <oesystem.h>
using namespace OESystem;
const char *InterfaceData =
"!PARAMETER -i\n"
" !TYPE string\n"
" !KEYLESS 1\n"
" !BRIEF Input file name\n"
"!END\n"
"!PARAMETER -o\n"
" !TYPE string\n"
" !KEYLESS 2\n"
" !BRIEF Output file name\n"
"!END\n";
int main(int argc, char** argv)
{
OEInterface itf(InterfaceData, argc, argv);
std::cout << "Input file name = " << itf.Get<std::string>("-i") << std::endl;
std::cout << "Output file name = " << itf.Get<std::string>("-o") << std::endl;
return 0;
}
!KEYLESS
parameters can still be specified using their command
line flag. However, positional arguments need to be the last values on
the command line. The following demonstrates how !KEYLESS
parameters can and cannot be specified on the command line:
prompt> ./UsingOEInterfaceKeyless -i foo -o bar
Input file = foo
Output file = bar
prompt> ./UsingOEInterfaceKeyless foo bar
Input file = foo
Output file = bar
prompt> ./UsingOEInterfaceKeyless -i foo bar
Input file = foo
Output file = bar
prompt> ./UsingOEInterfaceKeyless foo -o bar
Fatal: Unknown parameter, foo, on command line
Warning
An ambiguity occurs when mixing !KEYLESS
with !LIST
. There
is no way for the parser to differentiate between multiple values
for a !LIST
and different !KEYLESS
parameters.
OEInterface resolves this by requiring that any
keyless list parameter must be the last keyless parameter.
Molecule Parameters¶
As the previous example illustrated how to handle simple parameters
such as float
, int
or string
.
The OEInterface also provides a convenient way to
open a molecule file and return its first molecule.
Listing 7: Example of using molecule parameter
#include <openeye.h>
#include <oeplatform.h>
#include <oechem.h>
using namespace OESystem;
using namespace OEChem;
const char *InterfaceData =
"!PARAMETER -mol\n"
"!TYPE OEGraphMol\n"
" !BRIEF Molecule file\n"
" !REQUIRED true\n"
" !KEYLESS 1\n"
"!END\n";
int main(int argc, char** argv)
{
OERegisterMolParameters();
OEInterface itf(InterfaceData, argc, argv);
OEGraphMol mol = itf.Get<OEGraphMol>("-mol");
std::cout << "Number of heavy atoms in molecule = " << OECount(mol, OEIsHeavy()) << std::endl;
return 0;
}
prompt> ./OEInterfaceMolParam -mol benzene.mol
Number of heavy atoms in molecule = 6
Similarly the first multi-conformer molecule can be imported from a molecule
file by setting the type of a parameter as !TYPE OEMol
.
Command Line Help¶
The OEInterface object can have a significant
amount of information, default values, text descriptions, etc., about
the parameters it holds. All this information allows the
OEInterface to provide help to the user
interactively automatically based upon the interface
definition. Listing 8
is a fairly
simple program, it just concatenates all the molecules given into a
single molecule. Though it has a fairly complex set of command line
parameters which OEInterface handles
automatically.
Listing 8: A program to concatenate any number of molecules together
#include <openeye.h>
#include <oesystem.h>
#include <oechem.h>
using namespace OESystem;
using namespace OEChem;
const char *InterfaceData =
"!BRIEF UsingOEInterfaceHelp [-d <delimiter>] [-o] <output> [-i] <input1> <input2> ...\n"
"!PARAMETER -delim\n"
" !ALIAS -d\n"
" !TYPE string\n"
" !DEFAULT _\n"
" !BRIEF Title delimiter\n"
" !DETAIL\n"
" This is the value given to the OEChem function, OEAddMols, to\n"
" separate the titles of the input molecules in the output.\n"
"!END\n"
"!PARAMETER -output\n"
" !ALIAS -o\n"
" !TYPE string\n"
" !REQUIRED true\n"
" !KEYLESS 1\n"
" !BRIEF The output file\n"
" !DETAIL\n"
" The molecule file to output to, can be any file format that\n"
" OEChem supports.\n"
"!END\n"
"!PARAMETER -inputs\n"
" !ALIAS -i\n"
" !TYPE string\n"
" !REQUIRED true\n"
" !LIST true\n"
" !KEYLESS 2\n"
" !BRIEF The input files\n"
" !DETAIL\n"
" A list of molecule files to add together. Note, only the\n"
" first molecule from every file will be added.\n"
"!END\n";
int main(int argc, char** argv)
{
OEInterface itf(InterfaceData, argc, argv);
OEGraphMol outmol;
for (OEIter<const std::string> i = itf.GetList<std::string>("-inputs"); i; ++i)
{
oemolistream ims(*i);
OEGraphMol inmol;
OEReadMolecule(ims, inmol);
OEAddMols(outmol, inmol, itf.Get<std::string>("-delim").c_str());
}
oemolostream oms(itf.Get<std::string>("-output"));
OEWriteMolecule(oms, outmol);
return 0;
}
Note
!BRIEF
can be used outside of a!PARAMETER
to give a concise one line command line usage.!ALIAS
is used to specify an alternative name the parameter can be specified with.!DETAIL
is used for a more detailed description of the parameter.
Whenever there are !REQUIRED
parameters the
OEInterface will complain about an empty command
line. This is usually the first way users interact with a command line
program so the goal is to give them only enough information to get the
program running. This is where specifying a top-level !BRIEF
is
exceptionally useful to the end user. The following is the output of
the program in Listing 8
when no
parameters are given:
prompt> ./UsingOEInterfaceHelp
No arguments specified on the command line
./UsingOEInterfaceHelp : UsingOEInterfaceHelp [-d <delimiter>] [-o] <output> [-i] <input1> <input2> ...
Required parameters:
-output : The output file
-inputs : The input files
For more help type:
./UsingOEInterfaceHelp --help
More information is accessible from the command line by specifying
--help
as the first argument on the command line. The following is
an example of requesting help from the program in Listing
7
:
prompt> ./UsingOEInterfaceHelp --help
./UsingOEInterfaceHelp : UsingOEInterfaceHelp [-d <delimiter>] [-o] <output> [-i] <input1> <input2> ...
Simple parameter list
-delim : Title delimiter
-inputs : The input files
-output : The output file
Additional help functions:
./UsingOEInterfaceHelp --help simple : Get a list of simple parameters (as seen above)
./UsingOEInterfaceHelp --help all : Get a complete list of parameters
./UsingOEInterfaceHelp --help defaults : List the defaults for all parameters
./UsingOEInterfaceHelp --help <parameter> : Get detailed help on a parameter
./UsingOEInterfaceHelp --help html : Create an html help file for this program
As seen above --help
can take arguments that allow the user
introspect even further into the semantics of a particular parameter.
prompt> ./UsingOEInterfaceHelp --help -inputs
Contents of parameter -inputs
Aliases : -i
Type : string
Allow list : true
Default : (parameter does not have a default)
Keyless : 2
Simple : true
Required : true
Brief : The input files
Detail
A list of molecule files to add together. Note, only the
first molecule from every file will be added.
Note
OECheckHelp
is the low-level function used to
check a command line for --help
. By default the
OEInterface
constructor
and the
OEParseCommandLine
function call
OECheckHelp
automatically so most users will
never need to use this function directly.
Interface Definition Specification¶
Configuring an OEInterface is the process of
telling the name, type, and other details of all the command line
parameters the program is going to use. An interface definition
consists of a collection of PARAMETER
blocks optionally organized
into CATEGORY
blocks. A PARAMETER
block must always have the
following information:
- NAME
The command line flag, e.g.,
-i
,-str
,-cutoff
- TYPE
The kind of information being passed, e.g.,
float
,int
,string
,bool
A PARAMETER
can also optionally contain the following additional
information:
- ALIAS
One or more aliases for the parameter name that are generally used to create shorthand for lengthy parameter names. For example a parameter named
-energy_cutoff_window
could be given an alias-ecw
, that would make typing-energy_cutoff_window 10.0
on the command line equivalent to-ecw 10.0
.- DEFAULT
A default value for the parameter that will be used if the parameter is not specified by the user on the command line.
- REQUIRED
A flag specifying that the parameter is required. If a parameter is required the program will throw an error if the user does not specify that parameter. By default parameters are not required.
- LIST
A flag specifying whether the parameter is a list of multiple values. For example,
./program -inputs file1.smi file2.smi file3.smi
.- KEYLESS
A number,
N
, that specifies that this parameter may be fulfilled by theNth
value on the command line that does not have a parameter flag specifically associated with it. For example, if-i
had aKEYLESS
value of1
specified the following are equivalent command lines,./program -i foo.smi
and./program foo.smi
.- VISIBILITY
A visibility level that controls when the parameter appears when the users requests
--help
. Valid values are:simple
,normal
,hidden
. The default issimple
if aVISIBILITY
isn’t specified.- LEGAL_VALUE
A valid value that the parameter can be. The parameter specified must be one of the values specified in a
LEGAL_VALUE
field.- ILLEGAL_VALUE
A value that cannot be specified as the parameter value.
- LEGAL_RANGE
A range that the parameter value must fall into. More useful than
LEGAL_VALUE
when the parameter is afloat
.- ILLEGAL_RANGE
A range that the parameter value must not fall into. More useful than
ILLEGAL_VALUE
when the parameter is afloat
.- BRIEF
A one line description of the parameter that will be displayed to users when listing multiple command line options using the high-level
--help
functions.- DETAIL
A multi line description of the parameter that will be displayed to users when the user requests help about a specific parameter, e.g.,
--help -foo
.
Therefore, a PARAMETER
block takes the following form:
!PARAMETER <name> [order priority]
!ALIAS <alias>
!TYPE <type>
!DEFAULT <default value>
!REQUIRED <true or false>
!LIST <true or false>
!KEYLESS <N>
!VISIBILITY <visibility>
!LEGAL_VALUE <value>
!ILLEGAL_VALUE <value>
!LEGAL_RANGE <hi value> <low value>
!ILLEGAL_RANGE <hi_value> <low_value>
!BRIEF <brief description>
!DETAIL
<detailed description line 1>
<detailed description line 2>
<detailed description line 3>
.
.
.
!END
PARAMETER
blocks can then be organized into CATEGORY
blocks. CATEGORY
blocks can also contain nested CATEGORY
blocks, there is no limit to the amount of nesting. The value
specified after the !CATEGORY
specifier is the name of the
CATEGORY
. The CATEGORY
can also contain a !BRIEF
to give a
human readable description of the grouping. The following interface
definition defines an interface definition with an “Input and Output”
category with sub-categories for input and output respectively. As
well as a category for various other parameters.
!CATEGORY Input and Output
!CATEGORY Input
!PARAMETER -i
...
!END
!CATEGORY Output
!PARAMETER -o
...
!END
!END
!END
!CATEGORY Other Stuff
!PARAMETER -o
...
!END
!END
By default parameters and categories appear in alphabetical order. This order can be adjusted however by adding an integer after the category or parameter name in the interface definition. Parameters and categories are then sorted first by this integer and secondarily by their names. If this integer is unspecified, it is assumed to be zero. The following example illustrates re-ordering of both categories and variables.
!CATEGORY Input and Output 1
!CATEGORY Input 1
!PARAMETER -input 1
...
!END
!PARAMETER -dbase 2
...
!END
!CATEGORY Output 2
!PARAMETER -output 1
...
!END
!PARAMETER -hits 2
...
!END
!END
!END
!CATEGORY Other Stuff 2
!PARAMETER -o
...
!END
!END