1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 04:33:53 +00:00
Henry Fredrick Schreiner 1ee6529530 Adding a bit to the readme
2017-02-17 09:16:56 -05:00
2017-02-15 12:17:49 -05:00
2017-02-11 09:38:35 -05:00
2017-02-15 12:17:49 -05:00
2017-02-15 09:35:54 -05:00
2017-02-12 13:01:43 -05:00
2017-01-26 16:48:30 -05:00
2017-02-14 12:21:11 -05:00
2017-02-12 13:01:43 -05:00
2017-02-17 09:16:56 -05:00

Build Status Join the chat at https://gitter.im/CLI11gitter/Lobby License: MIT

CLI11: Command line parser for C++11

CLI11 provides all the features you expect in a powerful command line parser, with a beautiful, minimal syntax and no dependencies beyond C++11. It is header only, and comes in a single file form for easy inclusion in projects. It is easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks. It is tested on Travis with a suite of tests, and is being included in the GooFit GPU fitting framework. It was inspired by plumbum.cli for Python. It has both a user friendly introduction here, as well as API documentation generated by Travis.

Why write another CLI parser?

An acceptable CLI parser library should be all of the following:

  • Easy to include (i.e., header only, one file if possible, no external requirements): While many programs depend on Boost, that should not be a requirement if all you want is CLI parsing.
  • Short Syntax: This is one of the main points of a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
  • C++11 or better: Should work with GCC 4.7+ (such as GCC 4.8 on CentOS 7) or above, or Clang 3.5+. (Note: for CLI11, Clang 3.4 only fails because of tests, googlemock does not support it.)
  • Work at least on Linux and MacOS.
  • Well tested using Travis.
  • Clear help printing.
  • Standard shell idioms supported naturally, like grouping flags, a positional separator, etc.
  • Easy to execute, with help, parse errors, etc. providing correct exit and details.
  • Easy to extend as part of a framework that provides "applications" to users.
  • Usable subcommands.
  • Produce real values that can be used directly in code, not something you have pay compute time to look up, for HPC applications.

The major CLI parsers for C++ include:

  • Boost Program Options: A great library if you already depend on Boost, but its pre-C++11 syntax is really odd and setting up the correct call in the main function is poorly documented (and is nearly a page of code). A simple wrapper for the Boost library was originally developed, but was discarded as CLI11 became more powerful. The idea of capturing a value and setting it originated with Boost PO.
  • The Lean Mean C++ Option Parser: One header file is great, but the syntax is atrocious, in my opinion. It was quite impractical to wrap the syntax or to use in a complex project. It seems to handle standard parsing quite well.
  • TCLAP: The not-quite-standard command line parsing causes common shortcuts to fail. It also seems to be poorly supported, with only minimal bugfixes accepted. Header only, but in quite a few files. Has not managed to get enough support to move to GitHub yet. No subcommands. Produced wrapped values.
  • Cxxopts: C++11, single file, and nice CMake support, but requires regex, therefore GCC 4.8 (CentOS 7 default) does not work. Syntax closely based on Boost PO, so not ideal but familiar.

So, this library was designed to provide a great syntax, good compiler compatibility, and minimal installation fuss.

Current status:

This library was built to supply the Application object for the GooFit CUDA/OMP fitting library. Before version 2.0 of GooFit is released, this library will reach version 1.0 status. The current tasks still planned are:

  • Expand tests to include a few more features
  • Improve documentation
  • Collect user feedback
  • Evaluate compatibility with ROOT's TApplication object.

See the changelog or GitHub releases for details.

Installing

To use, there are two methods:

  1. Copy CLI11.hpp from the most recent release into your include directory, and you are set. This is combined from the source files for every release.
  2. Checkout the repository and add as a subdirectory for CMake. You can use the CLI interface target. (CMake 3.4+ recommended)

To build the tests, checkout the repository and use CMake:

mkdir build
cd build
cmake ..
make
GTEST_COLOR=1 CTEST_OUTPUT_ON_FAILURE=1 make test

Adding options

To set up, add options, and run, your main function will look something like this:

CLI::App app{"App description"};

std::string filename = "default";
app.add_option("-f,--file", file, "A help string");

try {
    app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
    return app.exit(e);
}

The initialization is just one line, adding options is just two each. The try/catch block ensures that -h,--help or a parse error will exit with the correct return code. (The return here should be inside main). After the app runs, the filename will be set to the correct value if it was passed, otherwise it will be set to the default. You can check to see if this was passed on the command line with app.count("--file").

The supported values are:

app.add_options(option_name,
                variable_to_bind_to, // int, float, vector, or string-like
                help_string="",
                default=false)

app.add_flag(option_name,
             int_or_bool = nothing,
             help_string="")

app.add_set(option_name,
            variable_to_bind_to,
            set_of_possible_options,
            help_string="",
            default=false)

app.add_config(option_name,
               default_file_name="",
               help_string="Read an ini file",
               required=false)

App* subcom = app.add_subcommand(name, discription);

An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using count, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in true for the final parameter for add_option or add_set.

Adding a configuration option is special. If it is present, it will be read along with the normal command line arguments. The file will be read if it exists, and does not throw an error unless required is true.

Example

  • "one,-o,--one": Valid as long as not a flag, would create an option that can be specified positionally, or with -o or --option
  • "this" Can only be passed positionally
  • "-a,-b,-c" No limit to the number of non-positional option names

The add commands return a pointer to an internally stored Option. If you set the final argument to true, the default value is captured and printed on the command line with the help flag. This option can be used directly to check for the count (->count()) after parsing to avoid a string based lookup. Before parsing, you can set the following options:

  • ->required(): The program will quit if this option is not present. This is mandatory in Plumbum, but required options seems to be a more standard term. For compatibility, ->mandatory() also works.
  • ->expected(N): Take N values instead of as many as possible, only for vector args
  • ->requires(opt): This option requires another option to also be present, opt is an Option pointer
  • ->excludes(opt): This option cannot be given with opt present, opt is an Option pointer
  • ->envname(name): Gets the value from the environment if present and not passed on the command line
  • ->group(name): The help group to put the option in. No effect for positional options. Defaults to "Options".
  • ->check(CLI::ExistingFile): Requires that the file exists if given
  • ->check(CLI::ExistingDirectory): Requires that the directory exists
  • ->check(CLI::NonexistentPath): Requires that the path does not exist
  • ->check(CLI::Range(min,max)): Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.

These options return the Option pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature bool(std::string). If you want to change the default help option, it is available through get_help_ptr.

On the command line, options can be given as:

  • -a (flag)
  • -abc (flags can be combined)
  • -f filename (option)
  • -ffilename (no space required)
  • -abcf filename (flags and option can be combined)
  • --long (long flag)
  • --file filename (space)
  • --file=filename (equals)

Extra positional arguments will cause the program to exit, so at least one positional option with a vector is recommended if you want to allow extraneous arguments If -- is present in the command line, everything after that is positional only.

Subcommands

Subcommands are naturally supported, with an infinite depth. To add a subcommand, call the add_subcommand method with a name and an optional description. This gives a pointer to an App that behaves just like the main app, and can take options or further subcommands.

All Apps have a get_subcommand() method, which returns a pointer to the subcommand passed on the command line, or nullptr if no subcommand was given. A simple compare of this pointer to each subcommand allows choosing based on subcommand. For many cases, however, using an app's callback may be easier. Every app executes a callback function after it parses; just use a lambda function (with capture to get parsed values) to .add_callback. If you throw CLI::Success, you can even exit the program through the callback. The main App has a callback slot, as well, but it is generally not as useful.

Subclassing

The App class was designed allow toolkits to subclass it, to provide default options and setup/teardown code. Subcommands remain an unsubclassed App, since those are not expected to need setup and teardown. The default App only adds a help flag, -h,--help, but provides an option to disable it in the constructor (and in add_subcommand).

Also, in a related note, the App you get a pointer to is stored in the parent App in a unique_ptrs (like Options) and are deleted when the main App goes out of scope.

How it works

Every add_ option you have seen so far depends on one method that takes a lambda function. Each of these methods is just making a different lambda function with capture to populate the option. The function has full access to the vector of vector of strings, so it knows how many times an option was passed, and how many arguments each passing received (flags add empty strings to keep the counts correct). The lambda returns true if it could validate the option strings, and false if it failed.

Contributing

To contribute, open an issue or pull request on GitHub, or ask a question on gitter.

Description
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface. https://cliutils.github.io/CLI11/book/
Readme 10 MiB
Languages
C++ 95.2%
CMake 3.4%
Python 0.8%
Shell 0.5%