npm 中文文档 npm 中文文档
指南
npmjs.com (opens new window)
指南
npmjs.com (opens new window)
  • 快速入门

    • npm 是什么?
    • npm 安装和更新
    • npm 防止权限错误
    • npm package.json 文件
    • npm 安装包
    • npm 更新包
    • npm 卸载包
    • npm 创建 Node.js 模块
    • npm 发布和更新包
    • npm 使用语义化版本
    • npm 使用 Dist-tags 标记包
    • npm 包和模块的了解
  • 命令行
  • 配置 npm

clipp - command line interfaces for modern C++


Easy to use, powerful and expressive command line argument handling for C++11/14/17 contained in a single header file.

options, options+value(s), positional values, positional commands, nested alternatives, decision trees, joinable flags, custom value filters, ...

documentation generation (usage lines, man pages); error handling

lots of examples; large set of tests

### Quick Reference Table

### Overview (short examples)

### Detailed Examples

#### Why yet another library for parsing command line arguments? / Design goals

#### Requirements / Compilers

Quick Intro


Simple Use Case — Simple Setup!


Consider this command line interface:

  1. ``` roff
  2. SYNOPSIS
  3.     convert <input file> [-r] [-o <output format>] [-utf16]

  4. OPTIONS
  5.     -r, --recursive  convert files recursively
  6.     -utf16           use UTF-16 encoding
  7. ```

Here is the code that defines the positional value input file and the three options -r, -o and -utf16. If parsing fails, the above default man page-like snippet will be printed to stdout.

  1. ``` sh
  2. #include <iostream>
  3. #include "clipp.h"
  4. using namespace clipp; using std::cout; using std::string;

  5. int main(int argc, char* argv[]) {
  6.     bool rec = false, utf16 = false;
  7.     string infile = "", fmt = "csv";

  8.     auto cli = (
  9.         value("input file", infile),
  10.         option("-r", "--recursive").set(rec).doc("convert files recursively"),
  11.         option("-o") & value("output format", fmt),
  12.         option("-utf16").set(utf16).doc("use UTF-16 encoding")
  13.     );

  14.     if(!parse(argc, argv, cli)) cout << make_man_page(cli, argv[0]);
  15.     // ...
  16. }
  17. ```

A More Complex Example:


  1. ``` roff
  2. SYNOPSIS
  3.     finder make <wordfile> -dict <dictionary> [--progress] [-v]
  4.     finder find <infile>... -dict <dictionary> [-o <outfile>] [-split|-nosplit] [-v]
  5.     finder help [-v]

  6. OPTIONS
  7.     --progress, -p           show progress                      
  8.     -o, --output <outfile>   write to file instead of stdout
  9.     -split, -nosplit         (do not) split output
  10.     -v, --version            show version
  11. ```

This CLI has three alternative commands (make, find, help ), some positional value-arguments (<wordfile>, <infile> ) of which one is repeatable, a required flag with value-argument (-dict <dictionary> ), an option with value-argument (-o <outfile> ), one option with two alternatives (-split, -nosplit ) and two conventional options (-v, --progress ).

Here is the code that defines the interface, generates the man page snippet above andhandles the parsing result:

  1. ``` sh
  2. using namespace clipp; using std::cout; using std::string;

  3. //variables storing the parsing result; initialized with their default values
  4. enum class mode {make, find, help};
  5. mode selected = mode::help;
  6. std::vector<string> input;
  7. string dict, out;
  8. bool split = false, progr = false;

  9. auto dictionary = required("-dict") & value("dictionary", dict);

  10. auto makeMode = (
  11.     command("make").set(selected,mode::make),
  12.     values("wordfile", input),
  13.     dictionary,
  14.     option("--progress", "-p").set(progr) % "show progress" );

  15. auto findMode = (
  16.     command("find").set(selected,mode::find),
  17.     values("infile", input),
  18.     dictionary,
  19.     (option("-o", "--output") & value("outfile", out)) % "write to file instead of stdout",
  20.     ( option("-split"  ).set(split,true) |
  21.       option("-nosplit").set(split,false) ) % "(do not) split output" );

  22. auto cli = (
  23.     (makeMode | findMode | command("help").set(selected,mode::help) ),
  24.     option("-v", "--version").call([]{cout << "version 1.0\n\n";}).doc("show version")  );

  25. if(parse(argc, argv, cli)) {
  26.     switch(selected) {
  27.         case mode::make: /* ... */ break;
  28.         case mode::find: /* ... */ break;
  29.         case mode::help: cout << make_man_page(cli, "finder"); break;
  30.     }
  31. } else {
  32.      cout << usage_lines(cli, "finder") << '\n';
  33. }
  34. ```

Quick Reference


Below are a few examples that should give you an idea for how clipp works. Consider this basic setup with a few variables that we want to set using command line arguments:

  1. ``` sh
  2. int main(int argc, char* argv[]) {
  3.     using namespace clipp;

  4.     // define some variables
  5.     bool a = false, b = false;
  6.     int n = 0, k = 0;
  7.     double x = 0.0, y = 0.0;
  8.     std::vector<int> ids;

  9.     auto cli = ( /* CODE DEFINING COMMAND LINE INTERFACE GOES HERE */ );

  10.     parse(argc, argv, cli);    //excludes argv[0]

  11.     std::cout << usage_lines(cli, "exe") << '\n';
  12. }
  13. ```

Interface ( usage_lines ) Code (content of cli parentheses )
:--- :--- :--- :--- :--- :---
exe [-a] option("-a", "--all").set(a)
exe [--all] option("--all", "-a", "--ALL").set(a)
exe [-a] [-b] option("-a").set(a), option("-b").set(b)
exe -a required("-a").set(a)
exe [-a] -b option("-a").set(a), required("-b").set(b)
exe [-n <times>] option("-n", "--iter") & value("times", n)
exe [-n [<times>]] option("-n", "--iter") & opt_value("times", n)
exe -n <times> required("-n", "--iter") & value("times", n)
exe -n [<times>] required("-n", "--iter") & opt_value("times", n)
exe [-c <x> <y>] option("-c") & value("x", x) & value("y", y)
exe -c <x> <y> required("-c") & value("x", x) & value("y", y)
exe -c <x> [<y>] required("-c") & value("x", x) & opt_value("y", y)
exe [-l <lines>...] option("-l") & values("lines", ids)
exe [-l [<lines>...]] option("-l") & opt_values("lines", ids)
exe [-l <lines>]... repeatable( option("-l") & value("lines", ids) )
exe -l <lines>... required("-l") & values("lines", ids)
exe -l [<lines>...] required("-l") & opt_values("lines", ids)
exe (-l <lines>)... repeatable( required("-l") & value("lines", ids) )
exe fetch [-a] command("fetch").set(k,1), option("-a").set(a)
`exe init fetch [-a]` `command("init").set(k,0) (command("fetch").set(k,1), option("-a").set(a))`
`exe [-a-b] ` `option("-a").set(a) option("-b").set(b)`
`exe [-m ab]` `option("-m") & (required("a").set(a) required("b").set(b))`

Overview


See the examples section for detailed explanations of each topic.

Namespace qualifiers are omitted from all examples for better readability. All entities are defined in namespace clipp.

Basic Setup


  1. ``` sh
  2. int main(int argc, char* argv[]) {
  3.     using namespace clipp;

  4.     auto cli = ( /* CODE DEFINING COMMAND LINE INTERFACE GOES HERE */ );
  5.     parse(argc, argv, cli);    //excludes argv[0]

  6.     //if you want to include argv[0]
  7.     //parse(argv, argv+argc, cli);
  8. }
  9. ```

There are two kinds of building blocks for command line interfaces: parameters and groups. Convieniently named factory functions produce parameters or groups with the desired settings applied.

Parameters (flag strings, commands, positional values, required flags, repeatable parameters)


  1. ``` sh
  2. bool a = false, f = false;
  3. string s; vector<string> vs;
  4. auto cli = (                             // matches  required  positional  repeatable
  5.     command("push"),                     // exactly      yes       yes         no
  6.     required("-f", "--file").set(f),     // exactly      yes       no          no
  7.     required("-a", "--all", "-A").set(a),  // exactly      no        no          no

  8.     value("file", s),                    // any arg      yes       yes         no
  9.     values("file", vs),                  // any arg      yes       yes         yes
  10.     opt_value("file", s),                // any arg      no        yes         no
  11.     opt_values("file", vs),              // any arg      no        yes         yes

  12.     //"catch all" parameter - useful for error handling
  13.     any_other(vs),                       // any arg      no        no          yes
  14.     //catches arguments that fulfill a predicate and aren't matched by other parameters
  15.     any(predicate, vs)                   // predicate    no        no          yes
  16. );
  17. ```

The functions above are convenience factories:

  1. ``` sh
  2. bool f = true; string s;
  3. auto v1 = values("file", s);
  4. // is equivalent to:
  5. auto v2 = parameter{match::nonempty}.label("file").blocking(true).repeatable(true).set(s);

  6. auto r1 = required("-f", "--file").set(f);
  7. // is equivalent to:
  8. auto r2 = parameter{"-f", "--file"}.required(true).set(f);
  9. ```

a required parameter has to match at least one command line argument
a repeatable parameter can match any number of arguments
non-positional (=non-blocking) parameters can match arguments in any order
a positional (blocking) parameter defines a "stop point", i.e., until it matches all parameters following it are not allowed to match; once it matched, all parameters preceding it (wihtin the current group) will become unreachable

Flags + Values

If you want parameters to be matched in sequence, you can tie them together using either operator & or the grouping function in_sequence :

  1. ``` sh
  2. int n = 1; string s; vector<int> ls;
  3. auto cli = (
  4.     //option with required value
  5.     option("-n", "--repeat") & value("times", n),

  6.     //required flag with optional value
  7.     required("--file") & opt_value("name", s),

  8.     //option with exactly two values
  9.     option("-p", "--pos") & value("x") & value("y"),

  10.     //same as before                   v            v
  11.     in_sequence( option("-p", "--pos") , value("x") , value("y") ),

  12.     //option with at least one value (and optionally more)
  13.     option("-l") & values("lines", ls)
  14. );
  15. ```

Filtering Value Parameters

Value parameters use a filter function to test if they are allowed to match an argument string. The default filter match::nonempty that is used by value, values, opt_value and opt_values will match any non-empty argument string. You can either supply other filter functions/function objects as first argument of value, values, etc. or use one of these built-in shorthand factory functions covering the most common cases:

  1. ``` sh
  2. string name; double r = 0.0; int n = 0;
  3. auto cli = (
  4.     value("user", name),   // matches any non-empty string
  5.     word("user", name),    // matches any non-empty alphanumeric string
  6.     number("ratio", r),    // matches string representations of numbers
  7.     integer("times", n)    // matches string representations of integers
  8. );
  9. ```

Analogous to value, opt_value, etc. there are also functions for words, opt_word, etc.

Value Parameters With Custom Filters

  1. ``` sh
  2. auto is_char = [](const string& arg) { return arg.size() == 1 && std::isalpha(arg[0]); };

  3. char c = ' ';
  4.                              // matches       required  positional  repeatable
  5. value(is_char, "c", c);      // one character  yes       yes         no
  6. ```

Groups


group mutually compatible parameters with parentheses and commas:

  1. ``` sh
  2. auto cli = ( option("-a"), option("-b"), option("-c") );
  3. ```

group mutually exclusive parameters as alternatives using operator | or one_of :

  1. ``` sh
  2. auto cli1 = ( value("input_file") | command("list") | command("flush") );

  3. auto cli2 = one_of( value("input_file") , command("list") , command("flush") );
  4. ```

group parameters so that they must be matched in sequence using operator & or in_sequence :

  1. ``` sh
  2. double x = 0, y = 0, z = 0;
  3. auto cli1 = ( option("-pos") & value("X",x) & value("Y",y) & value("Z",z) );

  4. auto cli2 = in_sequence( option("-pos") , value("X",x) , value("Y",y) , value("Z",z) );
  5. ```

Note that surrounding groups are not affected by this, so that -a and -b can be matched in any order while -b and the value X must match in sequence:

  1. ``` sh
  2. bool a = false, b = false; int x = 0;
  3. auto cli = (  option("-a").set(a),  option("-b").set(b) & value("X",x)  );
  4. ```

groups can be nested and combined to form arbitrarily complex interfaces (see here and here ):

  1. ``` sh
  2. auto cli = ( command("push") | ( command("pull"), option("-f", "--force") )  );
  3. ```

groups can be repeatable as well:

  1. ``` sh
  2. auto cli1 = repeatable( command("flip") | command("flop") );
  3. ```

force common prefixes on a group of flags:

  1. ``` sh
  2. int x = 0;
  3. auto cli1 = with_prefix("-", option("a"), option("b") & value("x",x), ... );
  4.                           // =>     -a           -b     ^unaffected^

  5. auto cli2 = with_prefix_short_long("-", "--", option("a", "all"), option("b"), ... );
  6.                                                // => -a  --all           -b
  7. ```

force common suffixes on a group of flags:

  1. ``` sh
  2. int x = 0;
  3. auto cli1 = with_suffix("=", option("a") & value("x",x), ... );
  4.                           // =>      a=    ^unaffected^

  5. auto cli2 = with_suffix_short_long(":", ":=", option("a", "all"), option("b"), ... );
  6.                                                // =>  a:   all:=          b:
  7. ```

make a group of flags joinable :

  1. ``` sh
  2. auto cli1 = joinable( option("-a"), option("-b"));  //will match "-a", "-b", "-ab", "-ba"

  3. //works also with arbitrary common prefixes:
  4. auto cli2 = joinable( option("--xA0"), option("--xB1"));  //will also match "--xA0B1" or "--xB1A0"
  5. ```

Interfacing With Your Code


The easiest way to connect the command line interface to the rest of your code is to bind object values or function (object) calls to parameters (see also here ):

  1. ``` sh
  2. bool b = false; int i = 5; int m = 0; string x; ifstream fs;
  3. auto cli = (
  4.     option("-b").set(b),                      // "-b" detected -> set b to true
  5.     option("-m").set(m,2),                    // "-m" detected -> set m to 2
  6.     option("-x") & value("X", x),             // set x's value from arg string
  7.     option("-i") & opt_value("i", i),         // set i's value from arg string  
  8.     option("-v").call( []{ cout << "v"; } ),  // call function (object) / lambda
  9.     option("-v")( []{ cout << "v"; } ),       // same as previous line
  10.     option("-f") & value("file").call([&](string f){ fs.open(f); })
  11. );
  12. ```

In production code one would probably use a settings class:

  1. ``` sh
  2. struct settings { bool x = false; /* ... */ };

  3. settings cmdline_settings(int argc, char* argv[]) {
  4.     settings s;
  5.     auto cli = ( option("-x").set(s.x), /* ... */ );
  6.     parse(argc, argv, cli);
  7.     return s;
  8. }
  9. ```

Note that the target must either be:

a fundamental type (int, long int, float, double, ... )
a type that is convertible from const char*
a callable entity: function, function object / lambda that either has an empty parameter list or exactly one parameter that is convertible from const char*

Generating Documentation (see also here)


Docstrings for groups and for parameters can either be set with the member function doc or with operator % :

  1. ``` sh
  2. auto cli = (
  3.     (   option("x").set(x).doc("sets X"),
  4.         option("y").set(y) % "sets Y"        
  5.     ),
  6.     "documented group 1:" % (  
  7.         option("-g").set(g).doc("acti
Last Updated: 2023-07-14 08:35:53