CLI11

C++11以降のためのコマンドラインパーサー。豊富な機能セットとシンプルで直感的なインターフェースを提供します。

cppclicommand-lineheader-only

GitHub概要

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.

スター3,754
ウォッチ45
フォーク379
作成日:2017年1月25日
言語:C++
ライセンス:Other

トピックス

clicli-parsercpp11no-dependencies

スター履歴

CLIUtils/CLI11 Star History
データ取得日時: 2025/7/25 02:06

フレームワーク

CLI11

概要

CLI11は、C++11以降に対応したモダンなコマンドラインパーサーライブラリです。豊富な機能セットとシンプルで直感的なインターフェースを提供し、ヘッダーオンリーのライブラリとして単一ファイルでの組み込みが可能です。小規模なプロジェクトでも簡単に使えながら、複雑なコマンドラインプロジェクトにも対応できる強力な機能を備えています。Mac、Linux、Windows上で動作し、3つのシステムすべてで100%のテストカバレッジを誇ります。

詳細

CLI11は、GooFit GPU fitting frameworkで最初に使用され、Azure ActionsとGitHub Actionsでテストされている実績のあるライブラリです。Python向けのplumbum.cliからインスピレーションを得て開発されました。

最新動向(2024-2025年)

CLI11は活発に開発が続けられており、以下の最新機能が追加されています:

  • プリコンパイルモード: CLI11_PRECOMPILEDオプションによって事前コンパイル可能となり、インクリメンタルビルドの時間短縮が実現
  • 空のベクター出力サポート: 空のベクターの出力をサポート
  • 合計オプションポリシー: より広範囲に適用可能な合計オプションポリシーを追加
  • オプション引数の検証: 位置引数と区別するためのオプション引数検証機能
  • デフォルトパスバリデーター: 設定ファイルやファイル引数のためのデフォルトパス検証機能

主な特徴

  • ヘッダーオンリー: 単一のヘッダーファイル(CLI11.hpp)をドロップインするだけで使用可能
  • 豊富なデータ型サポート: string、int、double、bool、vector、variantなど多様な型に対応
  • サブコマンド: アプリケーションの機能を論理的に整理可能
  • オプションとフラグ: 短縮形(-v)と長形式(--verbose)の両方をサポート
  • 位置引数: 必須・オプション・配列引数に対応
  • 自動ヘルプ生成: コマンドライン引数から自動的にヘルプメッセージを生成
  • バリデーター: 入力値の検証機能を内蔵
  • 設定ファイル: TOML、INI、JSON形式の設定ファイルをサポート
  • シェル補完: Bash、Zsh、Fish、PowerShellの補完スクリプトを自動生成
  • C++11対応: モダンなC++機能を活用しつつ、C++11以降で動作

主要コンポーネント

  1. CLI::App: アプリケーションのメインクラス
  2. Options: コマンドラインオプションの定義と処理
  3. Arguments: 位置引数の処理
  4. Validators: 入力値の検証
  5. Config: 設定ファイルの読み込みと処理
  6. Formatter: ヘルプメッセージの書式設定

メリット・デメリット

メリット

  • 簡単な導入: ヘッダーオンリーで、単一ファイルをインクルードするだけで使用可能
  • 高性能: コンパイル時最適化により高速動作
  • 豊富な機能: バリデーション、サブコマンド、設定ファイルサポートなど
  • モダンなC++: C++11以降の機能を活用した直感的なAPI
  • クロスプラットフォーム: Windows、Linux、macOSで完全サポート
  • 充実したテスト: 100%のテストカバレッジで信頼性が高い
  • アクティブな開発: 継続的な機能追加とバグ修正
  • プリコンパイル対応: 大規模プロジェクトでのビルド時間短縮が可能

デメリット

  • C++11依存: 古いコンパイラでは使用できない
  • 学習コスト: 豊富な機能ゆえに、全機能を理解するには時間が必要
  • ヘッダーオンリーの制約: 大きなプロジェクトではコンパイル時間が増加する可能性(プリコンパイルモードで軽減可能)
  • ドキュメントの分散: 機能が豊富なため、必要な情報を見つけるのに時間がかかる場合がある

主要リンク

書き方の例

基本的な使用例

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

int main(int argc, char** argv) {
    CLI::App app{"MyApp - シンプルなCLIアプリケーション"};
    
    std::string filename = "default.txt";
    app.add_option("-f,--file", filename, "処理するファイル名");
    
    CLI11_PARSE(app, argc, argv);
    
    std::cout << "処理ファイル: " << filename << std::endl;
    return 0;
}

フラグとオプションの例

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

int main(int argc, char** argv) {
    CLI::App app{"設定可能なアプリケーション"};
    
    // フラグ(bool値)
    bool verbose = false;
    app.add_flag("-v,--verbose", verbose, "詳細な出力");
    
    // 必須オプション
    std::string name;
    app.add_option("-n,--name", name, "ユーザー名")->required();
    
    // デフォルト値付きオプション
    int count = 1;
    app.add_option("-c,--count", count, "実行回数", true);
    
    // 複数値を受け取るオプション
    std::vector<std::string> files;
    app.add_option("-f,--files", files, "処理ファイル一覧");
    
    CLI11_PARSE(app, argc, argv);
    
    if (verbose) {
        std::cout << "詳細モードが有効です" << std::endl;
    }
    
    std::cout << "ユーザー名: " << name << std::endl;
    std::cout << "実行回数: " << count << std::endl;
    
    if (!files.empty()) {
        std::cout << "処理ファイル:" << std::endl;
        for (const auto& file : files) {
            std::cout << "  - " << file << std::endl;
        }
    }
    
    return 0;
}

サブコマンドの例

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

int main(int argc, char** argv) {
    CLI::App app{"ファイル管理ツール"};
    
    // listサブコマンド
    auto list_cmd = app.add_subcommand("list", "ファイル一覧を表示");
    bool show_hidden = false;
    list_cmd->add_flag("-a,--all", show_hidden, "隠しファイルも表示");
    list_cmd->callback([&]() {
        std::cout << "ファイル一覧を表示中";
        if (show_hidden) std::cout << "(隠しファイル含む)";
        std::cout << std::endl;
    });
    
    // copyサブコマンド
    auto copy_cmd = app.add_subcommand("copy", "ファイルをコピー");
    std::string source, dest;
    copy_cmd->add_option("source", source, "コピー元ファイル")->required();
    copy_cmd->add_option("dest", dest, "コピー先ファイル")->required();
    bool force = false;
    copy_cmd->add_flag("-f,--force", force, "上書きを強制");
    copy_cmd->callback([&]() {
        std::cout << source << " から " << dest << " にコピー中";
        if (force) std::cout << "(強制上書き)";
        std::cout << std::endl;
    });
    
    // deleteサブコマンド
    auto delete_cmd = app.add_subcommand("delete", "ファイルを削除");
    std::vector<std::string> targets;
    delete_cmd->add_option("files", targets, "削除対象ファイル")->required();
    bool recursive = false;
    delete_cmd->add_flag("-r,--recursive", recursive, "再帰的に削除");
    delete_cmd->callback([&]() {
        for (const auto& target : targets) {
            std::cout << target << " を削除中";
            if (recursive) std::cout << "(再帰的)";
            std::cout << std::endl;
        }
    });
    
    CLI11_PARSE(app, argc, argv);
    return 0;
}

バリデーション付きオプション

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

int main(int argc, char** argv) {
    CLI::App app{"バリデーション例"};
    
    // ファイル存在チェック
    std::string input_file;
    app.add_option("-i,--input", input_file, "入力ファイル")
        ->required()
        ->check(CLI::ExistingFile);
    
    // 数値範囲チェック
    int port;
    app.add_option("-p,--port", port, "ポート番号")
        ->required()
        ->check(CLI::Range(1024, 65535));
    
    // カスタムバリデーター
    std::string email;
    app.add_option("-e,--email", email, "メールアドレス")
        ->check([](const std::string& str) {
            if (str.find('@') == std::string::npos) {
                return "有効なメールアドレスではありません";
            }
            return std::string();
        });
    
    // 選択肢制限
    std::string format;
    app.add_option("-f,--format", format, "出力形式")
        ->check(CLI::IsMember({"json", "xml", "csv", "yaml"}));
    
    CLI11_PARSE(app, argc, argv);
    
    std::cout << "入力ファイル: " << input_file << std::endl;
    std::cout << "ポート: " << port << std::endl;
    std::cout << "メール: " << email << std::endl;
    std::cout << "形式: " << format << std::endl;
    
    return 0;
}

設定ファイル対応の例

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

int main(int argc, char** argv) {
    CLI::App app{"設定ファイル対応アプリ"};
    
    // 設定ファイルオプション
    std::string config_file = "config.ini";
    app.add_option("-c,--config", config_file, "設定ファイル")
        ->check(CLI::ExistingFile);
    
    // 各種設定項目
    std::string host = "localhost";
    app.add_option("--host", host, "接続先ホスト");
    
    int port = 8080;
    app.add_option("--port", port, "接続ポート");
    
    bool ssl = false;
    app.add_flag("--ssl", ssl, "SSL接続を使用");
    
    std::vector<std::string> headers;
    app.add_option("--header", headers, "追加HTTPヘッダー");
    
    // 設定ファイルを読み込む
    try {
        app.set_config("--config", config_file, "設定ファイル", false);
    } catch(const CLI::FileError &e) {
        std::cout << "設定ファイルの読み込みに失敗: " << e.what() << std::endl;
    }
    
    CLI11_PARSE(app, argc, argv);
    
    std::cout << "接続先: " << host << ":" << port << std::endl;
    std::cout << "SSL: " << (ssl ? "有効" : "無効") << std::endl;
    
    if (!headers.empty()) {
        std::cout << "追加ヘッダー:" << std::endl;
        for (const auto& header : headers) {
            std::cout << "  " << header << std::endl;
        }
    }
    
    return 0;
}

プリコンパイルモードの使用例

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

find_package(CLI11 CONFIG REQUIRED)

# プリコンパイルモードを有効化
set(CLI11_PRECOMPILED ON)

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

エラーハンドリングの例

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

int main(int argc, char** argv) {
    CLI::App app{"エラーハンドリング例"};
    
    std::string input_file;
    app.add_option("-i,--input", input_file, "入力ファイル")
        ->required()
        ->check(CLI::ExistingFile);
    
    try {
        app.parse(argc, argv);
    } catch (const CLI::ParseError &e) {
        // CLIパースエラーのハンドリング
        return app.exit(e);
    } catch (const CLI::Success &e) {
        // --helpや--versionによる正常終了
        return CLI::ExitCodes::Success;
    } catch (const std::exception &e) {
        // その他の例外
        std::cerr << "エラーが発生しました: " << e.what() << std::endl;
        return CLI::ExitCodes::BaseClass;
    }
    
    // メイン処理
    std::cout << "ファイルを処理中: " << input_file << std::endl;
    
    return CLI::ExitCodes::Success;
}

単一ファイルでの使用例

# 最新リリースから単一ヘッダーファイルをダウンロード
wget https://github.com/CLIUtils/CLI11/releases/latest/download/CLI11.hpp

# または curl を使用
curl -LO https://github.com/CLIUtils/CLI11/releases/latest/download/CLI11.hpp
// main.cpp - 単一ファイルでの使用
#include "CLI11.hpp"
#include <iostream>

int main(int argc, char** argv) {
    CLI::App app{"単一ファイルアプリ"};
    
    std::string message = "Hello, World!";
    app.add_option("-m,--message", message, "表示メッセージ");
    
    CLI11_PARSE(app, argc, argv);
    
    std::cout << message << std::endl;
    return 0;
}
# コンパイルして実行
g++ -std=c++11 main.cpp -o myapp
./myapp --message "こんにちは、CLI11!"