CLI11

A command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.

cppclicommand-lineheader-only

GitHub Overview

CLIUtils/CLI11

CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.

Stars3,754
Watchers45
Forks379
Created:January 25, 2017
Language:C++
License:Other

Topics

clicli-parsercpp11no-dependencies

Star History

CLIUtils/CLI11 Star History
Data as of: 7/25/2025, 02:06 AM

Framework

CLI11

Overview

CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface. It comes in a single header-only file form for easy inclusion in projects. It's easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks. Works on Mac, Linux, and Windows, with 100% test coverage on all three systems.

Details

CLI11 was originally used by the GooFit GPU fitting framework and is tested on Azure Actions and GitHub Actions. It was inspired by plumbum.cli for Python.

Latest Updates (2024-2025)

CLI11 continues to be actively developed with the following recent features:

  • Pre-compiled Mode: CLI11_PRECOMPILED option allows pre-compilation of the library, saving time on incremental rebuilds
  • Empty Vector Output Support: Support for output of empty vectors
  • Summing Option Policy: A summing option policy that can be applied more broadly
  • Optional Arguments Validation: Option to validate optional arguments to discriminate from positional arguments
  • Default Path Validator: New validator to check for files on a default path for configuration files or other file arguments

Key Features

  • Header-Only: Simply drop in a single header file (CLI11.hpp) to use
  • Rich Data Type Support: Supports string, int, double, bool, vector, variant, and more
  • Subcommands: Organize application functionality logically
  • Options and Flags: Supports both short (-v) and long (--verbose) forms
  • Positional Arguments: Required, optional, and array arguments supported
  • Automatic Help Generation: Automatically generates help messages from command-line arguments
  • Validators: Built-in input validation functionality
  • Configuration Files: Supports TOML, INI, and JSON configuration files
  • Shell Completion: Auto-generates completion scripts for Bash, Zsh, Fish, and PowerShell
  • C++11 Compatible: Uses modern C++ features while remaining compatible with C++11+

Major Components

  1. CLI::App: Main application class
  2. Options: Command-line option definition and processing
  3. Arguments: Positional argument processing
  4. Validators: Input value validation
  5. Config: Configuration file loading and processing
  6. Formatter: Help message formatting

Pros and Cons

Pros

  • Easy Integration: Header-only, just include a single file
  • High Performance: Fast execution through compile-time optimization
  • Rich Features: Validation, subcommands, configuration file support, etc.
  • Modern C++: Intuitive API using C++11+ features
  • Cross-Platform: Full support for Windows, Linux, and macOS
  • Well-Tested: 100% test coverage for high reliability
  • Active Development: Continuous feature additions and bug fixes
  • Pre-compile Support: Reduced build times for large projects

Cons

  • C++11 Dependency: Cannot be used with older compilers
  • Learning Curve: Rich feature set requires time to fully understand
  • Header-Only Constraints: May increase compile times in large projects (mitigated by pre-compile mode)
  • Scattered Documentation: Finding specific information can take time due to extensive features

Key Links

Code Examples

Basic Usage

#include "CLI/CLI.hpp"
#include <iostream>

int main(int argc, char** argv) {
    CLI::App app{"MyApp - A simple CLI application"};
    
    std::string filename = "default.txt";
    app.add_option("-f,--file", filename, "File to process");
    
    CLI11_PARSE(app, argc, argv);
    
    std::cout << "Processing file: " << filename << std::endl;
    return 0;
}

Flags and Options Example

#include "CLI/CLI.hpp"
#include <iostream>

int main(int argc, char** argv) {
    CLI::App app{"Configurable Application"};
    
    // Flag (boolean value)
    bool verbose = false;
    app.add_flag("-v,--verbose", verbose, "Enable verbose output");
    
    // Required option
    std::string name;
    app.add_option("-n,--name", name, "User name")->required();
    
    // Option with default value
    int count = 1;
    app.add_option("-c,--count", count, "Execution count", true);
    
    // Multi-value option
    std::vector<std::string> files;
    app.add_option("-f,--files", files, "Files to process");
    
    CLI11_PARSE(app, argc, argv);
    
    if (verbose) {
        std::cout << "Verbose mode enabled" << std::endl;
    }
    
    std::cout << "User name: " << name << std::endl;
    std::cout << "Execution count: " << count << std::endl;
    
    if (!files.empty()) {
        std::cout << "Files to process:" << std::endl;
        for (const auto& file : files) {
            std::cout << "  - " << file << std::endl;
        }
    }
    
    return 0;
}

Subcommands Example

#include "CLI/CLI.hpp"
#include <iostream>

int main(int argc, char** argv) {
    CLI::App app{"File Management Tool"};
    
    // list subcommand
    auto list_cmd = app.add_subcommand("list", "Display file list");
    bool show_hidden = false;
    list_cmd->add_flag("-a,--all", show_hidden, "Show hidden files too");
    list_cmd->callback([&]() {
        std::cout << "Displaying file list";
        if (show_hidden) std::cout << " (including hidden files)";
        std::cout << std::endl;
    });
    
    // copy subcommand
    auto copy_cmd = app.add_subcommand("copy", "Copy files");
    std::string source, dest;
    copy_cmd->add_option("source", source, "Source file")->required();
    copy_cmd->add_option("dest", dest, "Destination file")->required();
    bool force = false;
    copy_cmd->add_flag("-f,--force", force, "Force overwrite");
    copy_cmd->callback([&]() {
        std::cout << "Copying from " << source << " to " << dest;
        if (force) std::cout << " (force overwrite)";
        std::cout << std::endl;
    });
    
    // delete subcommand
    auto delete_cmd = app.add_subcommand("delete", "Delete files");
    std::vector<std::string> targets;
    delete_cmd->add_option("files", targets, "Files to delete")->required();
    bool recursive = false;
    delete_cmd->add_flag("-r,--recursive", recursive, "Recursive deletion");
    delete_cmd->callback([&]() {
        for (const auto& target : targets) {
            std::cout << "Deleting " << target;
            if (recursive) std::cout << " (recursive)";
            std::cout << std::endl;
        }
    });
    
    CLI11_PARSE(app, argc, argv);
    return 0;
}

Validation Example

#include "CLI/CLI.hpp"
#include <iostream>

int main(int argc, char** argv) {
    CLI::App app{"Validation Example"};
    
    // File existence check
    std::string input_file;
    app.add_option("-i,--input", input_file, "Input file")
        ->required()
        ->check(CLI::ExistingFile);
    
    // Number range check
    int port;
    app.add_option("-p,--port", port, "Port number")
        ->required()
        ->check(CLI::Range(1024, 65535));
    
    // Custom validator
    std::string email;
    app.add_option("-e,--email", email, "Email address")
        ->check([](const std::string& str) {
            if (str.find('@') == std::string::npos) {
                return "Not a valid email address";
            }
            return std::string();
        });
    
    // Choice restriction
    std::string format;
    app.add_option("-f,--format", format, "Output format")
        ->check(CLI::IsMember({"json", "xml", "csv", "yaml"}));
    
    CLI11_PARSE(app, argc, argv);
    
    std::cout << "Input file: " << input_file << std::endl;
    std::cout << "Port: " << port << std::endl;
    std::cout << "Email: " << email << std::endl;
    std::cout << "Format: " << format << std::endl;
    
    return 0;
}

Configuration File Support

#include "CLI/CLI.hpp"
#include <iostream>

int main(int argc, char** argv) {
    CLI::App app{"Configuration File App"};
    
    // Configuration file option
    std::string config_file = "config.ini";
    app.add_option("-c,--config", config_file, "Configuration file")
        ->check(CLI::ExistingFile);
    
    // Various configuration items
    std::string host = "localhost";
    app.add_option("--host", host, "Connection host");
    
    int port = 8080;
    app.add_option("--port", port, "Connection port");
    
    bool ssl = false;
    app.add_flag("--ssl", ssl, "Use SSL connection");
    
    std::vector<std::string> headers;
    app.add_option("--header", headers, "Additional HTTP headers");
    
    // Load configuration file
    try {
        app.set_config("--config", config_file, "Configuration file", false);
    } catch(const CLI::FileError &e) {
        std::cout << "Failed to load configuration file: " << e.what() << std::endl;
    }
    
    CLI11_PARSE(app, argc, argv);
    
    std::cout << "Connection: " << host << ":" << port << std::endl;
    std::cout << "SSL: " << (ssl ? "enabled" : "disabled") << std::endl;
    
    if (!headers.empty()) {
        std::cout << "Additional headers:" << std::endl;
        for (const auto& header : headers) {
            std::cout << "  " << header << std::endl;
        }
    }
    
    return 0;
}

Pre-compiled Mode Usage

# CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(MyProject)

find_package(CLI11 CONFIG REQUIRED)

# Enable pre-compiled mode
set(CLI11_PRECOMPILED ON)

add_executable(my_app main.cpp)
target_link_libraries(my_app CLI11::CLI11)

Error Handling Example

#include "CLI/CLI.hpp"
#include <iostream>

int main(int argc, char** argv) {
    CLI::App app{"Error Handling Example"};
    
    std::string input_file;
    app.add_option("-i,--input", input_file, "Input file")
        ->required()
        ->check(CLI::ExistingFile);
    
    try {
        app.parse(argc, argv);
    } catch (const CLI::ParseError &e) {
        // Handle CLI parse errors
        return app.exit(e);
    } catch (const CLI::Success &e) {
        // Normal exit due to --help or --version
        return CLI::ExitCodes::Success;
    } catch (const std::exception &e) {
        // Other exceptions
        std::cerr << "An error occurred: " << e.what() << std::endl;
        return CLI::ExitCodes::BaseClass;
    }
    
    // Main processing
    std::cout << "Processing file: " << input_file << std::endl;
    
    return CLI::ExitCodes::Success;
}

Single File Usage

# Download single header file from latest release
wget https://github.com/CLIUtils/CLI11/releases/latest/download/CLI11.hpp

# Or use curl
curl -LO https://github.com/CLIUtils/CLI11/releases/latest/download/CLI11.hpp
// main.cpp - Single file usage
#include "CLI11.hpp"
#include <iostream>

int main(int argc, char** argv) {
    CLI::App app{"Single File App"};
    
    std::string message = "Hello, World!";
    app.add_option("-m,--message", message, "Message to display");
    
    CLI11_PARSE(app, argc, argv);
    
    std::cout << message << std::endl;
    return 0;
}
# Compile and run
g++ -std=c++11 main.cpp -o myapp
./myapp --message "Hello, CLI11!"