A single header C++ library for parsing command line arguments and options with minimal amount of code

Related tags

CLI quick_arg_parser
Overview

Try it online

Quick Arg Parser

Tired of unwieldy tools like getopt or argp? Quick Arg Parser is a single header C++ library for parsing command line arguments and options with minimal amount of code. All you have to do is to instantiate a class inheriting from the MainArguments type and access its members.

#include "quick_arg_parser.hpp"

struct Args : MainArguments {
	std::string folder = argument(0) = ".";
	int efficiency = option("efficiency", 'e', "The intended efficiency") = 5;
	bool verbose = option('v');
	std::vector<int> ports = option("port", p);
};

int main(int argc, char** argv) {
	Args args{{argc, argv}};
	if (args.verbose) {
		std::cout << "Folder " << args.folder << std::endl;
		std::cout << "Efficiency " << args.efficiency << std::endl;
	}
	// ...

And it can deal with the following call:

./a.out . --efficiency 9 -v -p 4242,6824

A longer example of usage is here.

More detailed information

The library requires C++11. I have tested it on GCC and Clang. C++11 does not allow aggregate-initialising parent classes, so the child class of MainArguments will have to inherit its constructor using MainArguments::MainArguments, allowing to create instances without double braces.

It should work on Windows, but the command-line arguments will be Unix-like (unless explicitly made different, see below).

It can parse:

  • integer types
  • floating point types
  • std::string
  • std::filesystem::path (if C++17 is available)
  • std::vector containing types that it can parse, expecting them to set multiple times (options only) or comma-separated
  • std::unordered_map indexed by std::string and containing types it can parse, expecting to be set as -pjob=3,work=5 -ptask=7
  • std::shared_ptr to types it can parse
  • std::unique_ptr to types it can parse
  • Optional (a clone of std::optional that can be implicitly converted to it if C++17 is available) of types it can parse
  • custom types if a parser for them is added (see below)

A class called Optional has to be used instead of std::optional (its usage is similar to std::optional and can be implicitly converted to it). If the option is missing, it will be empty; it won't compile with default arguments (except nullptr and std::nullopt).

Options are declared as follows:

TypeName varName = option("long_name", 'l', "Help entry") = "default value";

The long name of the option or the short name of the option can be omitted. No option is mandatory, if the option is not listed, it's zero, an empty string, an empty vector or a null pointer. The value when it's missing can be set by assigning into the option call. The help entry will be printed when calling the program with the --help option. It can be omitted.

Boolean options are true when the option is listed and false by default. Groups of boolean arguments can be written together, for example you can write -qrc instead of -q -r -c in the options. Other options expect a value to follow them.

Mandatory arguments are declared as follows:

TypeName varName = argument(0);

The number is the index of the argument, indexed from zero. The program name is not argument zero.

Optional arguments are declared as follows:

TypeName varName = argument(0) = "default value";

Anything behind a -- separator will be considered an argument, no matter how closely it resembles an option.

If you expect an unlimited number of arguments, you can access them all through MainArguments' public variable named arguments. The first one in the vector is the first argument after the program name.

To implement a behaviour where the first argument is actually a command, like with git, the arguments have to be parsed separately for each command. Quick Arg Parser does not facilitate this, but it can be used with it by dealing with the first argument through a usual if/else if group, then constructing MainArguments instantiations with {argc - 1, argv + 1}.

Automatic help entry

Calling the program with --help or -? will print the expected number of arguments and all options, also listing their help entries if set.

The description of the program and arguments can be altered by defining a method with signature static std::string help(const std::string&), which gets the program name as argument and is expected to output the first part of help. To replace the help for options, you need to define a method static std::string options().

By default, the program exits after printing help. This behaviour can be changed by defining a method with signature void onHelp() and it will be called instead.

Automatic version entry

If the class has an inline static string member called version or a method with signature static std::string version(), it will react to options --version or -V by printing the string and exiting. The automatic exit can be overriden by defining a void onVersion() method, which will be called instead.

Validation

You can add a lambda (or a class with overloaded function call operator) that takes the value and returns either a bool indicating if the value is valid or throws an exception if the value is invalid.

	int port = option("port", 'p').validator([] (int port) { return port > 1023; });

C++17

If C++17 is available, then the Optional type can be converted into std::optional. Because of a technical limitation, std::optional cannot be used as an argument type. Also, arguments can be deserialised into std::filesystem::path.

Legacy options

Sometimes, it's necessary to support options like -something or /something. This can be done using:

	int speed = nonstandardOption("-efficiency", 'e');

If this is done, the long option will not be expected to be exactly as listed in the first argument, not preceded by a double dash. If it starts with a single dash, it will not be considered an aggregate of short options.

For compatibility with atypical command line interfaces, setting an argument -p to 1024 can be done not only as -p 1024, but also as -p=1024 or -p1024. Also, if it's a long argument named --port, it can be written as --port=1024. A vector type argument can be alternatively written as multiple settings of the same option, for example -p 1024 -p1025.

Custom types

To support your custom class (called MyType here), define this somewhere before the definition of the parsing class:

namespace QuickArgParserInternals {
template <>
struct ArgConvertervoid> {
	static MyType makeDefault() {
		return {}; // Do something else if it doesn't have a default constructor
	}
	static MyType deserialise(const std::string& from) {
		return MyType::fromString(from); // assuming this is how it's deserialised
	}
	constexpr static bool canDo = true;
};
} // namespace

Gotchas

This isn't exactly the way C++ was expected to be used, so there might be a few traps for those who use it differently than intended. The class inheriting from MainArguments can have other members, but its constructor can be dangerous. Using the constructor to initialise members set through option or argument will cause the assignment to override the parsing behaviour for those members. The constructor also should not have side effects, because it will be called more than once, not always with the parsed values. Neither of this matters if you use it as showcased.

Because of consistency, using Optional as an argument type does not make that argument optional, you need to set nullptr or std::nullopt (C++17) as default argument to make it optional.

Issues
  • Fix access out of bounds for bool arguments.

    Fix access out of bounds for bool arguments.

    When using a long bool argument as last argument, you will push_back a null-pointer which is undefined behavior (and actually crashes for me) see https://en.cppreference.com/w/cpp/string/basic_string/basic_string (5)

    opened by Nicholas42 1
  • Bug when compiling with MSVC

    Bug when compiling with MSVC

    Godbolt: https://godbolt.org/z/74v6n3

    example.cpp
    <source>(9): error C2668: 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string': ambiguous call to overloaded function
    C:/data/msvc/14.28.29333/include\xstring(2496): note: could be 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(std::basic_string<char,std::char_traits<char>,std::allocator<char>> &&) noexcept'
    C:/data/msvc/14.28.29333/include\xstring(2413): note: or       'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(const _Elem *const )'
            with
            [
                _Elem=char
            ]
    C:/data/msvc/14.28.29333/include\xstring(2754): note: or       'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(std::initializer_list<_Elem>,const _Alloc &)'
            with
            [
                _Elem=char,
                _Alloc=std::allocator<char>
            ]
    <source>(9): note: while trying to match the argument list '(MainArguments<Input>::Grabber<MainArguments<Input>::DummyValidator>)'
    Compiler returned: 2
    

    The weird thing is that everything works fine with GCC and Clang.

    opened by mrexodia 1
  • Not Posix compliant, nor Gnu getopt_long compliant

    Not Posix compliant, nor Gnu getopt_long compliant

    Here is some test code derived from your short example: test.cpp.zip

    Here is its output: % ./a.out . --efficiency 9 -v Folder . Efficiency 9

    % ./a.out . -e 9 -v Folder . Efficiency 9

    These should also work according to the Posix getopt() spec, but they don't. % ./a.out . -e9 -v Switch group -e9 contains a switch that needs an argument

    % ./a.out . -ve 9 Switch group -ve contains a switch that needs an argument

    % ./a.out . -ve9 Switch group -ve9 contains a switch that needs an argument

    This should work, but it doesn't: % ./a.out . --efficiency=9 -v Unknown switch --efficiency=9

    opened by MtnViewJohn 1
  • Add support for counted nullary options using std::vector<bool>

    Add support for counted nullary options using std::vector

    I thought it would be cool to support counted flag options like the verbosity level flag in Gnu tar. Using the size of a vector may be a bit heavy-weight, but it's a simple change and leverages your existing vector support.

    opened by MtnViewJohn 0
  • std::vector specialization does not support multiple option instances

    std::vector specialization does not support multiple option instances

    test2.cpp.zip

    This is a slightly modified version of your short example. The efficiency option is now a std::vector

    Here is some output: % ./a.out -v -e 5 Folder . Efficiency 5

    % ./a.out -v -e 5,6 Folder . Efficiency 5 Efficiency 6

    So far, so good. But what about this? % ./a.out -v -e 5 -e 6 Folder . Efficiency 5

    This is unfortunate. I think that options that specialize std::vector should allowed to work whether they arguments are in one list or several.

    opened by MtnViewJohn 0
Owner
C++ developer (formerly physicist)
null
easy to use, powerful & expressive command line argument parsing for modern C++ / single header / usage & doc generation

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 sing

André Müller 886 Jun 25, 2022
null 76 Apr 18, 2022
EAMain provides a multi-platform entry point used for platforms that don't support console output, return codes and command-line arguments.

EAMain provides a multi-platform entry point used for platforms that don't support console output, return codes and command-line arguments.

Electronic Arts 30 May 17, 2022
Parse command line arguments by defining a struct

Parse command line arguments by defining a struct Quick Start #include <structopt/app.hpp> struct Options { // positional argument // e.g., .

Pranav 399 Jun 20, 2022
C++ getopt wrapper to make it easier to parse command line arguments

Options is a simple C++ wrapper for getopt that makes it easier to handle command line argument parsing in C++. See demo.cc and the Makefile for an e

Gary Hollis 1 Oct 30, 2021
Command-line flag parsing in C

flag.h Inspired by Go's flag module: https://pkg.go.dev/flag WARNING! The design of the library is not finished and may be a subject to change. Quick

Tsoding 36 Jun 26, 2022
Minimal but open SDK for developing small command line tools.

Minimal SDK for macOS This repository provides the basis to build a cross compiler for macOS. With it, you can compile small command line tools from a

Ayke 9 Mar 18, 2022
led is a line-oriented text editor in command line

led is a line-oriented text editor in command line. This editor is similar to the standard program on unix systems - GNU ed. But i'm not going to make an exact clone of that program, it's just a pet project.

Artem Mironov 9 Jun 14, 2022
A command parsing library

LampOpt操作文档 概述 LampOpt是一个基于C++的控制台命令解析库,优点是体型小、适应全平台、方便易用。 引用 可选择在IDE中直接在引用目录中添加odt.h,或直接与需编译文件放在同一目录下,并引用: #include "odt.h" 使用 odt.h头文件内定义了一个名为LampOp

东灯 4 Jan 9, 2022
A simple command line application in order to create new Code workspaces.

mkcws Summary A simple command line application in order to create new Code workspaces. License This project's license is GPL 2. The whole license tex

Kevin Matthes 0 Apr 1, 2022
A command-line tool to generate Linux manual pages from C source code.

mangen A command-line tool to generate Linux manual pages from C source code. Description mangen is, as said above, a program to generate Linux manual

null 2 Nov 15, 2021
nicegraf-shaderc is a command-line tool that transforms HLSL code into shaders for various graphics APIs.

User Manual Table of Contents Introduction Project Status Obtaining the Source Code and Building Running Defining Techniques Generated Header File Pip

nicebyte 88 Jun 20, 2022
Added more EXTRAM options and compatible devices

Improved External Ram functionality For Teensy4 Core Expaned the configurability of the external ram and added suport for more ram chips. NOTE: THIS H

Michael MacDonald 1 Nov 6, 2021
Shpp - Call c++ functions from a shell with any arguments of any types parsed automatically

shpp Call c++ functions from a shell with any arguments of any types parsed automatically Declare a variable or define a function and register it in s

Pedro Moreira 96 Jun 8, 2022
a version of lolcat with options for some lgbtq+ flags

queercat a version of lolcat with some lgbtq+ pride flags options Usage $ queercat [-f flag_number][-h horizontal_speed] [-v vertical_speed] [--] [FIL

null 16 Jan 16, 2022
C++ Library for pulling system and hardware information, without hitting the command line.

infoware C++ Library for pulling system and hardware information, without hitting the command line. Requirements No non-built-in ones by default. Some

The Phantom Derpstorm 287 Jun 27, 2022
A library for interactive command line interfaces in modern C++

cli A cross-platform header only C++14 library for interactive command line interfaces (Cisco style) Features Header only Cross-platform (linux and wi

Daniele Pallastrelli 803 Jul 2, 2022
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.

CLI11: Command line parser for C++11 What's new • Documentation • API Reference CLI11 is a command line parser for C++11 and beyond that provides a ri

null 2.1k Jun 24, 2022
A simple to use, composable, command line parser for C++ 11 and beyond

Clara v1.1.5 !! This repository is unmaintained. Go here for a fork that is somewhat maintained. !! A simple to use, composable, command line parser f

Catch Org 651 Jun 15, 2022