Awesome Open Source
Awesome Open Source

Parse command line arguments by defining a struct

ci status conan package ci status ci status codacy standard license

Quick Start

#include <structopt/app.hpp>

struct Options {
   // positional argument
   //   e.g., ./main <file>
   std::string config_file;

   // optional argument
   //   e.g., -b "192.168.5.3"
   //   e.g., --bind_address "192.168.5.3"
   //
   // options can be delimited with `=` or `:`
   // note: single dash (`-`) is enough for short & long option
   //   e.g., -bind_address=localhost
   //   e.g., -b:192.168.5.3
   //
   // the long option can also be provided in kebab case:
   //   e.g., --bind-address 192.168.5.3
   std::optional<std::string> bind_address;
 
   // flag argument
   // Use `std::optional<bool>` and provide a default value. 
   //   e.g., -v
   //   e.g., --verbose
   //   e.g., -verbose
   std::optional<bool> verbose = false;

   // directly define and use enum classes to limit user choice
   //   e.g., --log-level debug
   //   e.g., -l error
   enum class LogLevel { debug, info, warn, error, critical };
   std::optional<LogLevel> log_level = LogLevel::info;

   // pair argument
   // e.g., -u <first> <second>
   // e.g., --user <first> <second>
   std::optional<std::pair<std::string, std::string>> user;

   // use containers like std::vector
   // to collect "remaining arguments" into a list
   std::vector<std::string> files;
};
STRUCTOPT(Options, config_file, bind_address, verbose, log_level, user, files);

Create a structopt::app and parse the command line arguments into the Options struct:

int main(int argc, char *argv[]) {

  try {
  
    // Line of code that does all the work:
    auto options = structopt::app("my_app").parse<Options>(argc, argv);

    // Print out parsed arguments:

    // std::cout << "config_file  = " << options.config_file << "\n";
    // std::cout << "bind_address = " << options.bind_address.value_or("not provided") << "\n";
    // std::cout << "verbose      = " << std::boolalpha << options.verbose.value() << "\n";
    // ...

  } catch (structopt::exception& e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}

Now let's pass some arguments to this program:

[email protected]:~$ ./main config.csv file5.csv file6.json
config_file  = config.csv
bind_address = not provided
verbose      = false
log_level    = 1
user         = not provided
files        = { file5.csv file6.json }

[email protected]:~$ ./main config.csv --bind-address localhost:9000 -v -log-level error file1.txt file2.txt
config_file  = config.csv
bind_address = localhost:9000
verbose      = true
log_level    = 3
user         = not provided
files        = { file1.txt file2.txt }

[email protected]:~$ ./main config_2.csv --bind-address 192.168.7.3 -log-level debug file1.txt file3.txt file4.txt --user "John Doe" "[email protected]"
config_file  = config_2.csv
bind_address = 192.168.7.3
verbose      = false
log_level    = 0
user         = John Doe<[email protected]>
files        = { file1.txt file3.txt file4.txt }

Table of Contents

Getting Started

structopt is a header-only library. Just add include/ to your include_directories and you should be good to go. A single header file version is also available in single_include/.

Positional Arguments

Here's an example of two positional arguments: input_file and output_file. input_file is expected to be the first argument and output_file is expected to be the second argument

#include <structopt/app.hpp>

struct FileOptions {
  // Positional arguments
  // ./main <input_file> <output_file>
  std::string input_file;
  std::string output_file;
};
STRUCTOPT(FileOptions, input_file, output_file);



int main(int argc, char *argv[]) {

  try {
    auto options = structopt::app("my_app").parse<FileOptions>(argc, argv);

    // Print parsed arguments:
    std::cout << "\nInput file  : " << options.input_file << "\n";
    std::cout << "Output file : " << options.output_file << "\n";

  } catch (structopt::exception& e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}
[email protected]:~$ ./main foo.txt bar.csv

Input file  : foo.txt
Output file : bar.csv

[email protected]:~$ ./main foo.csv
Error: expected value for positional argument `output_file`.

USAGE: ./my_app input_file output_file

ARGS:
    input_file
    output_file

Optional Arguments

Now, let's look at optional arguments. To configure an optional argument, use std::optional in the options struct like below.

#include <structopt/app.hpp>

struct GccOptions {
  // language standard
  // e.g., -std=c++17
  // e.g., --std c++20
  std::optional<std::string> std = "c++11";

  // verbosity enabled with `-v` or `--verbose`
  // or `-verbose`
  std::optional<bool> verbose = false;

  // enable all warnings with `-Wall`
  std::optional<bool> Wall = false;

  // produce only the compiled code
  // e.g., gcc -C main.c
  std::optional<bool> Compile = false;

  // produce output with `-o <exec_name>`
  std::optional<std::string> output = "a.out";

  std::string input_file;
};
STRUCTOPT(GccOptions, std, verbose, Wall, Compile, output, input_file);


int main(int argc, char *argv[]) {
  try {
    auto options = structopt::app("gcc").parse<GccOptions>(argc, argv);

    // Print parsed arguments

    std::cout << "std        : " << options.std.value() << "\n";
    std::cout << "verbose    : " << std::boolalpha << options.verbose.value() << "\n";
    std::cout << "Wall       : " << std::boolalpha << options.Wall.value() << "\n";
    std::cout << "Compile    : " << std::boolalpha << options.Compile.value() << "\n";
    std::cout << "Output     : " << options.output.value() << "\n";
    std::cout << "Input file : " << options.input_file << "\n";
  } catch (structopt::exception &e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}

NOTE structopt supports two option delimiters, = and : for optional arguments. This is meaningful and commonly used in single-valued optional arguments, e.g., --std=c++17.

[email protected]:~$ ./main -C main.cpp
std        : c++11
verbose    : false
Wall       : false
Compile    : true
Output     : a.out
Input file : main.cpp

[email protected]:~$ ./main -std=c++17 -o main main.cpp
std        : c++17
verbose    : false
Wall       : false
Compile    : false
Output     : main
Input file : main.cpp

[email protected]:~$ ./main main.cpp -v -std:c++14 --output:main -Wall
std        : c++14
verbose    : true
Wall       : true
Compile    : false
Output     : main
Input file : main.cpp

NOTE In summary, for a field in your struct named bind_address, the following are all legal ways to provide a value:

  • Short form:
    • -b <value>
  • Long form:
    • --bind_address <value>
    • -bind_address <value>
  • Kebab case:
    • --bind-address <value>
    • -bind-address <value>
  • Equal ('=') option delimiter
    • -b=<value>
    • --bind_address=<value>
    • -bind_address=<value>
    • --bind-address=<value>
    • -bind-address=<value>
  • Colon ':' option delimiter
    • -b:<value>
    • --bind_address:<value>
    • -bind_address:<value>
    • --bind-address:<value>
    • -bind-address:<value>

Double dash (--) Argument

A double dash (--) is used in most bash built-in commands and many other commands to signify the end of command options, after which only positional parameters are accepted.

Example use: lets say you want to grep a file for the string -v - normally -v will be considered the option to reverse the matching meaning (only show lines that do not match), but with -- you can grep for string -v like this:

#include <structopt/app.hpp>

struct GrepOptions {
  // reverse the matching
  // enable with `-v`
  std::optional<bool> v = false;
  
  // positional arguments
  std::string search;
  std::string pathspec;
};
STRUCTOPT(GrepOptions, v, search, pathspec);



int main(int argc, char *argv[]) {

  try {
    auto options = structopt::app("my_app").parse<GrepOptions>(argc, argv);

    if (options.v == true) {
      std::cout << "`-v` provided - Matching is now reversed\n";
    }

    std::cout << "Search   : " << options.search << "\n";
    std::cout << "Pathspec : " << options.pathspec << "\n";
  }
  catch (structopt::exception& e) {
    std::cout << e.what();
    std::cout << e.help();
  }

}
[email protected]:~$ ./main -v foo bar.txt
`-v` provided - Matching is now reversed
Search   : foo
Pathspec : bar.txt

[email protected]:~$ ./main -- -v bar.txt
Search   : -v
Pathspec : bar.txt

Flag Arguments

Flag arguments are std::optional<bool> with a default value.

NOTE The default value here is important. It is not a flag if a default value isn't provided. It will simply be an optional argument.

NOTE If --verbose is a flag argument with a default value of false, then providing the argument will set it to true. If --verbose does not have a default value, then structopt will expect the user to provide a value, e.g., --verbose true.

#include <structopt/app.hpp>

struct Options {
  // verbosity flag
  // -v, --verbose
  // remember to provide a default value
  std::optional<bool> verbose = false;
};
STRUCTOPT(Options, verbose);



int main(int argc, char *argv[]) {
  auto options = structopt::app("my_app").parse<Options>(argc, argv);

  if (options.verbose == true) {
    std::cout << "Verbosity enabled\n";
  }
}
[email protected]:~$ ./main

[email protected]:~$ ./main -v
Verbosity enabled

[email protected]:~$ ./main --verbose
Verbosity enabled

Enum Class Arguments

Thanks to magic_enum, structopt supports enum classes. You can use an enum classes to ask the user to provide a value given a choice of values, restricting the possible set of allowed input arguments.

#include <structopt/app.hpp>

struct StyleOptions {
  enum class Color {red, green, blue};

  // e.g., `--color red`
  std::optional<Color> color = Color::red;
};
STRUCTOPT(StyleOptions, color);



int main(int argc, char *argv[]) {

  try {
    auto options = structopt::app("my_app").parse<StyleOptions>(argc, argv);

    // Use parsed argument `options.color`

    if (options.color == StyleOptions::Color::red) {
        std::cout << "#ff0000\n";
    }
    else if (options.color == StyleOptions::Color::blue) {
        std::cout << "#0000ff\n";
    }
    else if (options.color == StyleOptions::Color::green) {
        std::cout << "#00ff00\n";
    }

  } catch (structopt::exception& e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}
[email protected]:~$ ./main --color red
#ff0000

[email protected]:~$ ./main -c blue
#0000ff

[email protected]:~$ ./main --color green
#00ff00

[email protected]:~$ ./main -c black
Error: unexpected input `black` provided for enum argument `color`. Allowed values are {red, green, blue}

USAGE: ./my_app [OPTIONS]

OPTIONS:
    -c, --color <color>

Tuple Arguments

Now that we've looked at enum class support, let's build a simple calculator. In this sample, we will use an std::tuple to pack all the arguments to the calculator:

#include <structopt/app.hpp>

struct CalculatorOptions {

  // types of operations supported
  enum class operation { add, subtract, multiply, divide };

  // single tuple positional argument
  std::tuple<operation, int, int> input;

};
STRUCTOPT(CalculatorOptions, input);



int main(int argc, char *argv[]) {

  try {
    auto options = structopt::app("my_app").parse<CalculatorOptions>(argc, argv);

    auto op = std::get<0>(options.input);
    auto lhs = std::get<1>(options.input);
    auto rhs = std::get<2>(options.input);
    switch(op)
    {
        case CalculatorOptions::operation::add:
            std::cout << lhs + rhs << "\n";
            break;
        case CalculatorOptions::operation::subtract:
            std::cout << lhs - rhs << "\n";
            break;
        case CalculatorOptions::operation::multiply:
            std::cout << lhs * rhs << "\n";
            break;
        case CalculatorOptions::operation::divide:
            std::cout << lhs / rhs << "\n";
            break;
    }
  }
  catch (structopt::exception& e) {
    std::cout << e.what();
    std::cout << e.help();
  }

}
[email protected]:~$ ./main add 1 2
3

[email protected]:~$ ./main subtract 5 9
-4

[email protected]:~$ ./main multiply 16 5
80

[email protected]:~$ ./main divide 1331 11
121

[email protected]:~$ ./main add 5
Error: failed to correctly parse tuple `input`. Expected 3 arguments, 2 provided.

USAGE: my_app input

ARGS:
    input

Vector Arguments

structopt supports gathering "remaining" arguments at the end of the command, e.g., for use in a compiler:

$ compiler file1 file2 file3

Do this by using an std::vector<T> (or other STL containers with .push_back(), e.g, std::deque or std::list).

NOTE Vector arguments have a cardinality of 0..*, i.e., zero or more arguments. Unlike array types, you can provide zero arguments to a vector and structopt will (try to) not complain.

#include <structopt/app.hpp>

struct CompilerOptions {
  // Language standard
  // e.g., --std c++17
  std::optional<std::string> std;

  // remaining arguments
  // e.g., ./compiler file1 file2 file3
  std::vector<std::string> files{};
};
STRUCTOPT(CompilerOptions, std, files);



int main(int argc, char *argv[]) {
  try {
    auto options = structopt::app("my_app").parse<CompilerOptions>(argc, argv);

    std::cout << "Standard : " << options.std.value_or("not provided") << "\n";
    std::cout << "Files    : { ";
    std::copy(options.files.begin(), options.files.end(),
              std::ostream_iterator<std::string>(std::cout, " "));
    std::cout << "}" << std::endl;
  } catch (structopt::exception &e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}

NOTE Notice below that the act of gathering remaining arguments is arrested as soon as an optional argument is detected. See the output of ./main file1.cpp file2.cpp --std c++17 below. Notice that --std=c++17 is not part of the vector. This is because --std is a valid optional argument.

[email protected]:~$ ./main
Standard : not provided
Files    : { }

[email protected]:~$ ./main file1.cpp file2.cpp
Standard : not provided
Files    : { file1.cpp file2.cpp }

[email protected]:~$ ./main file1.cpp file2.cpp --std=c++17
Standard : c++17
Files    : { file1.cpp file2.cpp }

[email protected]:~$ ./main --std:c++20 file1.cpp file2.cpp
Standard : c++20
Files    : { file1.cpp file2.cpp }

Compound Arguments

Compound arguments are optional arguments that are combined and provided as a single argument. Example: ps -aux

#include <structopt/app.hpp>

struct Options {
  // Flag arguments
  std::optional<bool> a = false;
  std::optional<bool> b = false;

  // Optional argument
  // e.g., -c 1.1 2.2
  std::optional<std::array<float, 2>> c = {};
};
STRUCTOPT(Options, a, b, c);



int main(int argc, char *argv[]) {
  try {
    auto options = structopt::app("my_app").parse<Options>(argc, argv);

    // Print parsed arguments:

    std::cout << std::boolalpha << "a = " << options.a.value()
              << ", b = " << options.b.value() << "\n";
    if (options.c.has_value()) {
      std::cout << "c = [" << options.c.value()[0] << ", " << options.c.value()[1]
                << "]\n";
    }
  } catch (structopt::exception &e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}
[email protected]:~$ ./main -ac 3.14 2.718
a = true, b = false
c = [3.14, 2.718]

[email protected]:~$ ./main -ba
a = true, b = true

[email protected]:~$ ./main -c 1.5 3.0 -ab
a = true, b = true
c = [1.5, 3]

Parsing Numbers

Integer Literals

structopt supports parsing integer literals including hexadecimal, octal, and binary notation.

#include <structopt/app.hpp>

struct IntegerLiterals {
  std::vector<int> numbers;
};
STRUCTOPT(IntegerLiterals, numbers);

int main(int argc, char *argv[]) {
  try {
    auto options = structopt::app("my_app").parse<IntegerLiterals>(argc, argv);

    for (auto &n : options.numbers)
      std::cout << n << "\n";
  } catch (structopt::exception &e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}
[email protected]:~$ ./main 1 0x5B 071 0b0101 -35 +98
1
91
57
5
-35
98

Floating point Literals

As for floating point numbers, structopt supports parsing scientific notation (e/E-notation):

#include <structopt/app.hpp>

struct FloatLiterals {
  std::vector<float> numbers;
};
STRUCTOPT(FloatLiterals, numbers);

int main(int argc, char *argv[]) {
  try {
    auto options = structopt::app("my_app").parse<FloatLiterals>(argc, argv);

    for (auto &n : options.numbers)
      std::cout << n << "\n";
  } catch (structopt::exception &e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}
[email protected]:~$ ./main -3.15 +2.717 2E-4 0.1e2 .5 -.3 +5.999
-3.15
2.717
0.0002
10
0.5
-0.3
5.999

Nested Structures

With structopt, you can define sub-commands, e.g., git init args or git config [flags] args using nested structures.

  • Simply create a nested structure that inherits from structopt::sub_command
  • You can use <nested_struct_object>.has_value() to check if it has been invoked.

The following program support two sub-commands: config and init:

#include <structopt/app.hpp>

struct Git {
  // Subcommand: git config
  struct Config : structopt::sub_command {
    // flag argument `--global`
    std::optional<bool> global = false;

    // key-value pair, e.g., `user.name "John Doe"`
    std::array<std::string, 2> name_value_pair{};
  };
  Config config;

  // Subcommand: git init
  struct Init : structopt::sub_command {

    // required argument
    // repository name
    std::string name;
  };
  Init init;
};
STRUCTOPT(Git::Config, global, name_value_pair);
STRUCTOPT(Git::Init, name);
STRUCTOPT(Git, config, init);



int main(int argc, char *argv[]) {


  try {
    auto options = structopt::app("my_app").parse<Git>(argc, argv);

    if (options.config.has_value()) {
      // config was invoked
      std::cout << "You invoked `git config`:\n";
      std::cout << "Global : " << std::boolalpha << options.config.global.value() << "\n";
      std::cout << "Input  : (" << options.config.name_value_pair[0] << ", " << options.config.name_value_pair[1] << ")\n";
    }
    else if (options.init.has_value()) {
      // init was invoked
      std::cout << "You invoked `git init`:\n";
      std::cout << "Repository name : " << options.init.name << "\n";
    }


  } catch (structopt::exception& e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}
[email protected]:~$ ./main config user.email "[email protected]"
You invoked `git config`:
Global : false
Input  : (user.email, [email protected])

[email protected]:~$ ./main config user.name "John Doe" --global
You invoked `git config`:
Global : true
Input  : (user.name, John Doe)

[email protected]:~$ ./main init my_repo
You invoked `git init`:
Repository name : my_repo



[email protected]:~$ ./main -h

USAGE: my_app [OPTIONS] [SUBCOMMANDS]

OPTIONS:
    -h, --help <help>
    -v, --version <version>

SUBCOMMANDS:
    config
    init




[email protected]:~$ ./main config -h

USAGE: config [FLAGS] [OPTIONS] name_value_pair

FLAGS:
    -g, --global

OPTIONS:
    -h, --help <help>
    -v, --version <version>

ARGS:
    name_value_pair




[email protected]:~$ ./main init -h

USAGE: init [OPTIONS] name

OPTIONS:
    -h, --help <help>
    -v, --version <version>

ARGS:
    name

NOTE Notice in the above stdout that the -h help option supports printing help both at the top-level struct and at the sub-command level.

NOTE structopt does not allow to invoke multiple sub-commands. If one has already been invoked, you will see the following error:

[email protected]:~$ ./main config user.name "John Doe" init my_repo
Error: failed to invoke sub-command `init` because a different sub-command, `config`, has already been invoked.

Sub-Commands, Vector Arguments, and Delimited Positional Arguments

Here's a second example for nested structures with vector arguments and the double dash (--) delimiter

#include <structopt/app.hpp>

struct CommandOptions {
  struct Sed : structopt::sub_command {
    // --trace
    std::optional<bool> trace = false;

    // remaining args
    std::vector<std::string> args;

    // pattern
    std::string pattern;

    // file
    std::string file;
  };
  Sed sed;
};
STRUCTOPT(CommandOptions::Sed, trace, args, pattern, file);
STRUCTOPT(CommandOptions, sed);



int main(int argc, char *argv[]) {

  auto app = structopt::app("my_app");

  try {

    auto options = app.parse<CommandOptions>(argc, argv);

    if (options.sed.has_value()) {
      // sed has been invoked

      if (options.sed.trace == true) {
        std::cout << "Trace enabled!\n";
      }

      std::cout << "Args    : ";
      for (auto& a : options.sed.args) std::cout << a << " "; 
      std::cout << "\n";
      std::cout << "Pattern : " << options.sed.pattern << "\n";
      std::cout << "File    : " << options.sed.file << "\n";
    }
    else {
      std::cout << app.help();
    }

  } catch (structopt::exception &e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}
[email protected]:~$ ./main

USAGE: my_app [OPTIONS] [SUBCOMMANDS]

OPTIONS:
    -h, --help <help>
    -v, --version <version>

SUBCOMMANDS:
    sed



[email protected]:~$ ./main sed --trace X=1 Y=2 Z=3 -- 's/foo/bar/g' foo.txt
Trace enabled!
Args    : X=1 Y=2 Z=3
Pattern : s/foo/bar/g
File    : foo.txt

Printing Help

structopt will insert two optional arguments for the user: help and version.

  • Using -h or --help will print the help message and exit.
  • Using -v or --version will print the program version and exit.
#include <structopt/app.hpp>

struct Options {
  // positional arguments
  std::string input_file;
  std::string output_file;

  // optional arguments
  std::optional<std::string> bind_address;

  // remaining arguments
  std::vector<std::string> files;
};
STRUCTOPT(Options, input_file, output_file, bind_address, files);



int main(int argc, char *argv[]) {
  auto options = structopt::app("my_app", "1.0.3").parse<Options>(argc, argv);
}
[email protected]:~$ ./main -h

USAGE: my_app [OPTIONS] input_file output_file files

OPTIONS:
    -b, --bind-address <bind_address>
    -h, --help <help>
    -v, --version <version>

ARGS:
    input_file
    output_file
    files

[email protected]:~$ ./main -v
1.0.3

Printing CUSTOM Help

structopt allows users to provide a custom help messages. Simply pass in your custom help as a string argument to structopt::app

#include <structopt/app.hpp>

struct Options {
  // positional arguments
  std::string input_file;
  std::string output_file;

  // optional arguments
  std::optional<std::string> bind_address;

  // remaining arguments
  std::vector<std::string> files;
};
STRUCTOPT(Options, input_file, output_file, bind_address, files);

int main(int argc, char *argv[]) {

  try {
    const std::string& custom_help = "Usage: ./my_app input_file output_file [--bind-address BIND_ADDRESS] [files...]\n";
    auto options = structopt::app("my_app", "1.0.3", custom_help).parse<Options>(argc, argv);
  } catch (structopt::exception &e) {
    std::cout << e.what() << "\n";
    std::cout << e.help();
  }
}
[email protected]:~$ ./main -h
Usage: ./my_app input_file output_file [--bind-address BIND_ADDRESS] [files...]

Building Samples and Tests

git clone https://github.com/p-ranav/structopt
cd structopt
mkdir build && cd build
cmake -DSTRUCTOPT_SAMPLES=ON -DSTRUCTOPT_TESTS=ON ..
make

WinLibs + MinGW

For Windows, if you use WinLibs like I do, the cmake command would look like this:

[email protected]:~$ mkdir build && cd build
[email protected]:~$ cmake -G "MinGW Makefiles" -DCMAKE_CXX_COMPILER="C:/WinLibs/mingw64/bin/g++.exe" -DSTRUCTOPT_SAMPLES=ON -DSTRUCTOPT_TESTS=ON ..
[email protected]:~$ make

[email protected]:~$ .\tests\structopt_tests.exe
[doctest] doctest version is "2.3.5"
[doctest] run with "--help" for options
===============================================================================
[doctest] test cases:     54 |     54 passed |      0 failed |      0 skipped
[doctest] assertions:    393 |    393 passed |      0 failed |
[doctest] Status: SUCCESS!

Compiler Compatibility

  • Clang/LLVM >= 5
  • MSVC++ >= 14.11 / Visual Studio >= 2017
  • Xcode >= 10
  • GCC >= 9

Generating Single Header

python3 utils/amalgamate/amalgamate.py -c single_include.json -s .

Contributing

Contributions are welcome, have a look at the CONTRIBUTING.md document for more information.

License

The project is available under the MIT license.



Alternative Project Comparisons
Related Awesome Lists
Top Programming Languages

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
C Plus Plus (257,795
Command Line (132,084
Vector (10,061
Struct (5,545
Enum (2,687
Reflection (2,435
Type Safe (932
Header Only (902
Type Safety (510
Argument Parser (424
Modern Cpp (306
Arguments (185
Argparse (137
Single Header Lib (126
Clap (63
Header Library (8
Structopt (5