.. include:: ../js.rst .. _progarg-tutorial: ############################ Evaluating Program Arguments ############################ ICL's :ref:`ICLUtils` package contains the *program argument evaluation framework*, providing the following functionalities: * a simple and intuitive string-based description of allowed program arguments * an easy to use way to describe program arguments * a efficient parser for program arguments, that provides human-readable error messages * an automatically supported set of common default program arguments, such as **-version** or **-help** * a concise method :icl:`pa` that can be use to query whether a specific program argument has been given and what it’s sub arguments where Program Arguments and Sub-Arguments *********************************** In general, we distinguish between the term *program argument*, *sub argument* and *dangling arguments*. E.g. an image type conversion application **my-convert** could expect an input image file name, an output image file name and an argument **-size** that always gets an extra sub-argument which defines the target image size. A valid call to the program would then be something like:: my-convert -size 320x240 image.png image.jpg In this case, we classify the set of all given program arguments as follows: * **-size** as a *defined* argument (expecting one sub-argument) * **320x240** as a *sub*-argument * **image.png** and **image.jpg** as *dangling* arguments All program arguments are either *defined*, expecting 1,n or an arbitrary number of sub-arguments, *dandling* or *sub-arguments* of defined ones. Furthermore, arguments can be mandatory or optional. .. note:: The support for dangling arguments must be enabled explicitly by adding a boolean flag to the call to the program argument initialization function :icl:`pa_init`. Since it turned out that usually applications have no *dangling* arguments, this feature is deactivated by default in order to avoid that erroneously given program arguments are not detected because they are interpreted as dangling arguments. Defining allowed program arguments ********************************** Program arguments are defined by using the :icl:`pa_init` function. Usually, this function must not be called explicitly because it is called indirectly by the :icl:`ICLApplication` (or short: :icl:`ICLApp`) constructor. Actually the parameters are simply passed to :icl:`pa_init` if an :icl:`ICLApp` is used. :icl:`pa_init` always receives the main function's program argument and count and a single definition string:: int main(int n, char **args){ pa_init(n,args,definition string); .. } The definition string consists of space separated tokens of single argument definitions. If real space characters are needed, they must be escaped using a trailing backslash character (please note, that one has to use a double backslash within a c++-string to create a backslash character). Each single argument definition token consists of a pipe-symbol separated list of argument name alternatives, followed by an optional parameter list in round braces, e.g.:: pa_init(n,args,"-input|-i(filename) -size(outputsize)"); Each *defined* argument may have 0, n, or an arbitrary number of sub-arguments. If the argument has no arguments -- in this case the empty brace can also be left out -- it becomes a *flag*, that is internally represented by a boolean value that defines whether it was given (then the value is **true**) or not (the value is **false**). An arbitrary sub-argument count can be achieved by using the intuitive string "..." as parameter list:: pa_init(n,ppc,"-enable-preprocessing|-pp -input|-i(...) -output(filename)"); In general, each parameter-list is a comma separated list of tokens that can be a type name, a type name followed by "=" and a default value or an integer token, or the special token "...". Here are some examples: two integer sub-arguments:: "(int,int)" five sub-arguments with unspecified types:: "(5)" four sub-arguments, first of type float, second and third of any type and last of type int:: "(float,2,int)" two sub-arguments first of type int with default value "4" and second of type **Size** with default argument "VGA":: "(int=4,Size=VGA)" .. note:: 1. in case of using just a number of sub-arguments, no default value can be given 2. It is either possible to provide default values for all arguments or for non of them. So far there is no C++-like mechanism for this. Types """"" **Question:** What types are allowed/supported? **Answer:** All types! Actually, this sounds more advanced than it actually is. Internally, all sub-arguments are managed as strings. The sub-argument is intended as a type-hint for the program user, and it is used for the generation the program usage output, that is created automatically, if the program is called with a **-help** argument, or if the given set of program arguments int not compatible with the defined set. Therefore, we strongly recommend to use explicit type names such as "filename" or "format-string" rather than just an integer parameter count. Mandatory Arguments """"""""""""""""""" Some of your program arguments might be mandatory for you application. If, e.g., no image source is defined, your application cannot work properly. If you access program arguments or sub-arguments, that have no default value and are not given, an exception is thrown. Since in this case, the retrieved error message could be difficult to analyze, certain arguments can be set up to be mandatory. This is done by adding an "[m]"-token (for *mandatory*) in front of the program argument definition token:: pa_init(n,ppc,"-size(Size=VGA) -pp [m]-input|-i(2)"); Here, the application will not start unless the "-input" argument is given with 2 sub-argument. Those arguments that have default values, cannot be set to mandatory because the default value would never be used in this case. And logically, also flags -- arguments without sub-arguments -- cannot be set to mandatory. Accessing Program Arguments *************************** Given program arguments can be retrieved using the :icl:`pa` function that returns an instance of :icl:`ProgArg`, however :icl:`ProgArg` instances is most of the time not used explicitly. Instead, the :icl:`ProgArg` type provides many useful operators that allows for using it in a very convenient manner. The following source code shows some usage examples: .. literalinclude:: examples/pa.cpp :language: c++ :linenos: Calling Programs with given Arguments ************************************* Let's call the resulting program **example**. Obviously, the program accesses all program arguments, so calling:: ./example fails, giving the error message in line 10 that sub-argument 0 of the defined argument "-input" was tried to access, but is was neither given nor defined by a default argument. In real-life examples, those defined arguments should be declared mandatory. First of all, let us try to get a list of the allowed program arguments. The framework always provides the program arguments **-help** and **-version**. In turn, these cannot be used for own purposes. **-help** gives a formatted *usage* output, in this case:: usage: example [ARGS] -size|-s {optional} (Size=VGA) -index {optional} (int) -input|-i {optional} (*,*) -files {optional} (...) [arbitrary subargument count possible] -help shows this help text -version shows version and copyright information **-version** provides current version and license information. If an application has some special license, the license text can be set using the :icl:`pa_set_license` function. Optionally the usage information can be explained using an extra help text that can be set using the :icl:`pa_set_help_text` function. A valid (and quite complex) program call could look like:: ./example a b c -size SVGA d e -index 7 -i dc 0 -files *.cpp Here, the tokens "a", "b", "c", "d" and "e" are dangling arguments, because they are neither defined nor associated with sub-arguments to other defined ones. The defined argument "-size" uses "SVGA" as sub-argument, "-index" uses "7" as sub-argument and "-i" gets two sub-arguments "dc" and "0". Finally "-files" gets a list of sub-arguments expanded by the shell (if there are any .cpp-files in the current directory). Adding more Descriptions ************************ In some cases, some or all given program arguments need an extra description. This can be given using the :icl:`pa_explain` function. .. note:: It is very important that :icl:`pa_explain` is called **before** :icl:`pa_init` is invoked. Later calls to :icl:`pa_explain` have no effect. Calling the program .. literalinclude:: examples/pa2.cpp :language: c++ :linenos: With:: ./example-2 --help Shows the following usage output:: usage: pa2 [ARGS] This is an example program that has no function but it demonstrates how to add some program argument and program description -size|-s {optional} (Size=VGA) image size use for something -index {optional} (int) some index used for something else -input|-i {optional} (*,*) input definition, the first first arg defines the input grabber backend, second arg selects a certain device from this backend -help shows this help text -version shows version and copyright information