Command-line parsing in MRtrix is based on a set of fairly generic conventions to maximise consistency for end-users, and convenience for developers. A command is expected to accept a certain number of arguments, and a certain number of options. These are specified in the code using the MR::App::ARGUMENTS and MR::App::OPTIONS vectors. They are also used to generate the help page for the application, and it is therefore sensible to provide as much information in the description fields as neccessary for end-users to understand how to use the command.
Arguments and options are specified within the usage() function of each command (see Creating a new MRtrix command for details). Arguments are supplied as a vector of MR::App::Argument objects, and by default each MR::App::Argument is expected to have its value supplied on the command-line (although one argument can be made optional, or allowed to be supplied multiple times).
Options are supplied as a vector of MR::App::Option objects, and by default are optional (although they can be specified as 'required'). By default, only one instance of each option is allowed, but this can also be changed. Options may also accept additional arguments, which should be supplied immediately after the option itself.
Parsing of the command-line is done by first identifying any options supplied and inserting them into the option list, along with their corresponding arguments (if any). All remaining tokens are taken to be arguments, and inserted into the MR::App::argument list. Checks are performed at this stage to ensure the number of arguments and options supplied is consistent with that specified in the usage() function.
The values of these arguments and options can be retrieved within the application using the MR::App::argument list and the MR::App::option list. Note that in practice, the MR::App::get_options() method is a much more convenient way of querying the command-line options.
An overview of the basic contents of an application is given in Creating a new MRtrix command. The description and syntax of the command should be fully specified in the usage() function, and the various arguments and options can then be retrieved and used in the application. Each of these aspects is described below.
The role of the usage() function is to populate the MR::App::DESCRIPTION, MR::App::ARGUMENTS, and MR::App::OPTIONS vectors, and optionally the MR::App::VERSION, MR::App::AUTHOR, and MR::App::COPYRIGHT entries if default values are not suitable. The example below illustrates a basic usage() function:
As shown in the example, description entries are added to the MR::App::DESCRIPTION vector using the +
operator, and consist of const char*
strings. Each separate string will be formatted into a distinct paragraph on the help page.
As shown in the example, individual command-line arguments are added to the MR::App::ARGUMENTS vector as MR::App::Argument objects using the +
operator. As a minimum, each Argument is constructed with its short-hand name (used in the syntax line), and its description, which will appear in a separate listing after the syntax line.
By default, an Argument is typed as a string. There are a number of other types that can be used, and each type will perform its own checks on the values provided by the user. For example:
The type specifiers that can be used are:
std::vector<int>
.std::vector<float>
.In certain cases, it is possible that none of these types are suitable. In these cases, the default type ([type_text()](MR::App::Argument::type_text())) should be used, and any custom casting and checking will need to be performed explicitly by the programmer.
It is possible to specify at most one argument as being optional, by adding the optional() flag:
Finally, at most one argument can be specified as repeatable (in other words, a number of these arguments can be supplied one after the other). This is useful for example if the command can operate on multiple data sets sequentially:
Individual command-line options are added to the OPTIONS vector as MR::App::Option objects using the +
operator. As a minimum, each Option is constructed with its short-hand name (used on the command-line), and its description:
Similarly to arguments, options can be specified as repeatable:
Options can also be specified as required (by default, options are optional):
To handle additional arguments to an MR::App::Option, arguments can be added to the option as MR::App::Argument objects, again using the +
operator. Note that in this case the description field of the argument is ignored, and so does not need to be specified: These Arguments can be typed in the same way as regular MR::App::Arguments. For example:
Option objects are actually inserted into MR::App::OptionGroup objects; in the example above, new Options were added to the default MR::App::OptionGroup. It is possible to create new option groups that will appear under their own heading in the help page.
This makes it possible to define OptionGroup objects for commonly-used functions elsewhere in the code, and simply add them into the application when required. For example:
This is particularly useful since these options can then be handled by the relevant functions or classes, without the developer of the application needing to worry about them in any way. Following on from the example above, the 'funny_processing' code might declare a function 'do_funny_processing()', which could then retrieve any relevant parameters supplied by the user on the command-line, with no interaction with the body of the application using that function (other than adding the relevant OptionGroup to the OPTIONS list):
In some cases, it might be useful to break up an MR::App::OptionGroup into several distinct sub-groups, even though they would all conceptually belong to the same section. For instance, some options might only be required when processing input data, others when producing output data. Some applications might only need to process data without producing any, others might only produce data, and others might need to do both. To support this, developers can provide several OptionGroups with identical sections names; these will then be displayed together in the help page if used together in the corresponding application.
For example:
Argument and option values can be retrieved at any point within the application. Arguments are provided via the MR::App::argument vector, which is a std::vector<App::ParsedArgument>
. Their values can be retrieved using the subscript operator, indexed by their position on the command-line. For convenience, values can be obtained directly by implicit type-casting (i.e. simply by using the argument in a context that requires the relevant type - e.g. assignment). The following examples should clarify how this is done:
Option values are most easily retrieved using the MR::App::get_options() convenience function. This returns a MR::App::Options object that is to all intents and purposes identical in use to a std::vector<std::vector<App::ParsedArgument>>
: one vector of values for each matching option specified on the command-line (an option may be specified multiple times if it has been specified using allow_multiple()). The values are stored in the same way as the parsed arguments, and their values can therefore also be retrieved using implicit type-casting (see above). The example below should clarify this:
A common use-case is to set a variable from the command-line, defaulting to a set value otherwise. This can be done using the MR::App::get_option_value() convenience call. For example:
If for any reason the get_options() convenience function is not suitable, options can be accessed directly using the MR::App::option variable, which is a std::vector
of MR::App::ParsedOption objects, one per option in the same order as on the command-line. Note that in this case, option values will only be available as const char*
string arrays, and no bounds checking will be performed. For further details, refer to the MR::App::ParsedOption description page.