Boost.Program_options

Boostライブラリの一部として提供される強力なプログラムオプション処理ライブラリ。

cppclicommand-lineboost

フレームワーク

Boost.Program_options

概要

Boost.Program_optionsは、Boostライブラリの一部として提供される強力なプログラムオプション処理ライブラリです。Boostエコシステムの一部として長年使用されており、大規模プロジェクトでの実績が豊富です。コマンドライン引数、設定ファイル、環境変数からのオプション取得を統一的に扱えることが特徴です。

詳細

Boost.Program_optionsは、C++プログラムにおけるオプション処理のためのフル機能ライブラリです。コマンドライン引数だけでなく、設定ファイルや環境変数からも設定を読み込むことができ、これらを統合して管理できます。Boostライブラリの高い品質基準に基づいて開発されており、産業レベルのアプリケーションで広く使用されています。

主な特徴

  • 複数の入力ソース: コマンドライン、設定ファイル、環境変数を統合的に処理
  • 豊富な設定ファイル形式: INI形式を標準サポート、カスタム形式も対応可能
  • 型安全: C++の型システムを活用した安全なオプション処理
  • バリデーション: オプション値の検証機能を内蔵
  • 国際化対応: Unicode文字列のサポート
  • エラーハンドリング: 詳細で分かりやすいエラーメッセージ
  • 拡張性: カスタムオプション処理の実装が可能
  • Boost品質: 高い信頼性とパフォーマンス

メリット・デメリット

メリット

  • 統合的な設定管理: コマンドライン、設定ファイル、環境変数を統一的に処理
  • 高い安定性: Boostライブラリの一部として長年の実績
  • 豊富な機能: 高度なオプション処理機能を提供
  • 設定ファイル対応: INI形式などの設定ファイルを標準サポート
  • 拡張性: カスタマイズが容易で柔軟性が高い
  • ドキュメント: 充実した公式ドキュメント
  • プラットフォーム対応: 幅広いプラットフォームで動作

デメリット

  • 依存関係: Boostライブラリ全体への依存が必要(部分的な使用も可能)
  • 学習コスト: 豊富な機能ゆえに習得に時間がかかる
  • ライブラリサイズ: 軽量なソリューションが必要な場合には重い
  • コンパイル時間: Boostライブラリ特有のテンプレート使用によるコンパイル時間の増加

主要リンク

書き方の例

基本的な使用例

#include <boost/program_options.hpp>
#include <iostream>
#include <fstream>

namespace po = boost::program_options;

int main(int argc, char* argv[]) {
    try {
        po::options_description desc("使用可能なオプション");
        desc.add_options()
            ("help,h", "ヘルプメッセージを表示")
            ("input-file,i", po::value<std::string>(), "入力ファイル名")
            ("output-file,o", po::value<std::string>(), "出力ファイル名")
            ("verbose,v", "詳細な出力を有効にする")
            ("compression", po::value<int>()->default_value(10), "圧縮レベル (1-10)");

        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);

        if (vm.count("help")) {
            std::cout << desc << std::endl;
            return 0;
        }

        if (vm.count("input-file")) {
            std::cout << "入力ファイル: " << vm["input-file"].as<std::string>() << std::endl;
        } else {
            std::cout << "入力ファイルが指定されていません" << std::endl;
            return 1;
        }

        if (vm.count("output-file")) {
            std::cout << "出力ファイル: " << vm["output-file"].as<std::string>() << std::endl;
        }

        if (vm.count("verbose")) {
            std::cout << "詳細モードが有効です" << std::endl;
        }

        std::cout << "圧縮レベル: " << vm["compression"].as<int>() << std::endl;

    } catch(std::exception& e) {
        std::cerr << "エラー: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

設定ファイル対応の例

#include <boost/program_options.hpp>
#include <iostream>
#include <fstream>

namespace po = boost::program_options;

int main(int argc, char* argv[]) {
    try {
        // コマンドライン専用オプション
        po::options_description generic("一般オプション");
        generic.add_options()
            ("version,v", "バージョン情報を表示")
            ("help,h", "ヘルプメッセージを表示")
            ("config,c", po::value<std::string>()->default_value("config.ini"), 
             "設定ファイル名");

        // 設定ファイルでも使用可能なオプション
        po::options_description config("設定");
        config.add_options()
            ("host", po::value<std::string>()->default_value("localhost"), 
             "接続先ホスト")
            ("port", po::value<int>()->default_value(8080), 
             "ポート番号")
            ("timeout", po::value<int>()->default_value(30), 
             "タイムアウト(秒)")
            ("max-connections", po::value<int>()->default_value(100), 
             "最大接続数")
            ("log-level", po::value<std::string>()->default_value("info"), 
             "ログレベル")
            ("ssl-enabled", po::value<bool>()->default_value(false), 
             "SSL接続を有効にする");

        // コマンドライン表示用
        po::options_description cmdline_options;
        cmdline_options.add(generic).add(config);

        // 設定ファイル用
        po::options_description config_file_options;
        config_file_options.add(config);

        po::variables_map vm;
        
        // コマンドライン引数を解析
        po::store(po::parse_command_line(argc, argv, cmdline_options), vm);
        po::notify(vm);

        if (vm.count("help")) {
            std::cout << cmdline_options << std::endl;
            return 0;
        }

        if (vm.count("version")) {
            std::cout << "バージョン 1.0.0" << std::endl;
            return 0;
        }

        // 設定ファイルを読み込み
        std::string config_file = vm["config"].as<std::string>();
        std::ifstream ifs(config_file);
        if (ifs) {
            std::cout << "設定ファイル '" << config_file << "' を読み込み中..." << std::endl;
            po::store(po::parse_config_file(ifs, config_file_options), vm);
            po::notify(vm);
        } else {
            std::cout << "設定ファイル '" << config_file << "' が見つかりません。デフォルト値を使用します。" << std::endl;
        }

        // 設定値を表示
        std::cout << "サーバー設定:" << std::endl;
        std::cout << "  ホスト: " << vm["host"].as<std::string>() << std::endl;
        std::cout << "  ポート: " << vm["port"].as<int>() << std::endl;
        std::cout << "  タイムアウト: " << vm["timeout"].as<int>() << "秒" << std::endl;
        std::cout << "  最大接続数: " << vm["max-connections"].as<int>() << std::endl;
        std::cout << "  ログレベル: " << vm["log-level"].as<std::string>() << std::endl;
        std::cout << "  SSL: " << (vm["ssl-enabled"].as<bool>() ? "有効" : "無効") << std::endl;

    } catch(std::exception& e) {
        std::cerr << "エラー: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

位置引数と複数値の例

#include <boost/program_options.hpp>
#include <iostream>
#include <vector>

namespace po = boost::program_options;

int main(int argc, char* argv[]) {
    try {
        po::options_description desc("ファイル処理ツール");
        desc.add_options()
            ("help,h", "ヘルプメッセージを表示")
            ("files", po::value<std::vector<std::string>>()->multitoken(), 
             "処理するファイル一覧")
            ("include,I", po::value<std::vector<std::string>>()->composing(), 
             "インクルードディレクトリ")
            ("define,D", po::value<std::vector<std::string>>()->composing(), 
             "定義マクロ")
            ("output-dir,o", po::value<std::string>()->default_value("./output"), 
             "出力ディレクトリ")
            ("jobs,j", po::value<int>()->default_value(1), 
             "並列実行数")
            ("recursive,r", "再帰的に処理")
            ("dry-run,n", "実際には実行せず、実行予定の内容を表示");

        // 位置引数の設定
        po::positional_options_description p;
        p.add("files", -1);  // 無制限の位置引数

        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv)
                    .options(desc)
                    .positional(p)
                    .run(), vm);
        po::notify(vm);

        if (vm.count("help")) {
            std::cout << "使用法: " << argv[0] << " [オプション] ファイル1 ファイル2 ..." << std::endl;
            std::cout << desc << std::endl;
            return 0;
        }

        if (!vm.count("files")) {
            std::cerr << "エラー: 処理するファイルが指定されていません" << std::endl;
            return 1;
        }

        auto files = vm["files"].as<std::vector<std::string>>();
        std::cout << "処理ファイル:" << std::endl;
        for (const auto& file : files) {
            std::cout << "  " << file << std::endl;
        }

        if (vm.count("include")) {
            auto includes = vm["include"].as<std::vector<std::string>>();
            std::cout << "インクルードディレクトリ:" << std::endl;
            for (const auto& inc : includes) {
                std::cout << "  " << inc << std::endl;
            }
        }

        if (vm.count("define")) {
            auto defines = vm["define"].as<std::vector<std::string>>();
            std::cout << "定義マクロ:" << std::endl;
            for (const auto& def : defines) {
                std::cout << "  " << def << std::endl;
            }
        }

        std::cout << "出力ディレクトリ: " << vm["output-dir"].as<std::string>() << std::endl;
        std::cout << "並列実行数: " << vm["jobs"].as<int>() << std::endl;
        std::cout << "再帰処理: " << (vm.count("recursive") ? "有効" : "無効") << std::endl;
        std::cout << "ドライラン: " << (vm.count("dry-run") ? "有効" : "無効") << std::endl;

    } catch(std::exception& e) {
        std::cerr << "エラー: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

環境変数対応とカスタムバリデーション

#include <boost/program_options.hpp>
#include <iostream>
#include <regex>

namespace po = boost::program_options;

// カスタムバリデーション関数
void validate(boost::any& v, const std::vector<std::string>& values,
              std::string* target_type, int) {
    po::validators::check_first_occurrence(v, values, target_type);
    const std::string& s = po::validators::get_single_string(values);
    
    // メールアドレスのバリデーション
    std::regex email_pattern(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
    if (!std::regex_match(s, email_pattern)) {
        throw po::validation_error(po::validation_error::invalid_option_value);
    }
    
    v = boost::any(s);
}

struct email_address {
    std::string addr;
};

void validate(boost::any& v, const std::vector<std::string>& values,
              email_address* target_type, int) {
    po::validators::check_first_occurrence(v, values, target_type);
    const std::string& s = po::validators::get_single_string(values);
    
    std::regex email_pattern(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
    if (!std::regex_match(s, email_pattern)) {
        throw po::validation_error(po::validation_error::invalid_option_value);
    }
    
    v = boost::any(email_address{s});
}

int main(int argc, char* argv[]) {
    try {
        po::options_description desc("アプリケーション設定");
        desc.add_options()
            ("help,h", "ヘルプメッセージを表示")
            ("database-url", po::value<std::string>(), 
             "データベース接続URL (環境変数: DATABASE_URL)")
            ("admin-email", po::value<email_address>(), 
             "管理者メールアドレス")
            ("max-workers", po::value<int>()->default_value(4), 
             "最大ワーカー数 (環境変数: MAX_WORKERS)")
            ("debug-mode", po::value<bool>()->default_value(false), 
             "デバッグモード (環境変数: DEBUG_MODE)")
            ("api-key", po::value<std::string>(), 
             "APIキー (環境変数: API_KEY)");

        po::variables_map vm;
        
        // コマンドライン引数を解析
        po::store(po::parse_command_line(argc, argv, desc), vm);

        // 環境変数を解析
        po::store(po::parse_environment(desc, [](const std::string& env_var) {
            // 環境変数名をオプション名に変換
            if (env_var == "DATABASE_URL") return std::string("database-url");
            if (env_var == "MAX_WORKERS") return std::string("max-workers");
            if (env_var == "DEBUG_MODE") return std::string("debug-mode");
            if (env_var == "API_KEY") return std::string("api-key");
            return std::string("");
        }), vm);

        po::notify(vm);

        if (vm.count("help")) {
            std::cout << desc << std::endl;
            std::cout << std::endl << "環境変数:" << std::endl;
            std::cout << "  DATABASE_URL    データベース接続URL" << std::endl;
            std::cout << "  MAX_WORKERS     最大ワーカー数" << std::endl;
            std::cout << "  DEBUG_MODE      デバッグモード (true/false)" << std::endl;
            std::cout << "  API_KEY         APIキー" << std::endl;
            return 0;
        }

        // 必須パラメータのチェック
        if (!vm.count("database-url")) {
            std::cerr << "エラー: データベースURLが必要です (--database-url または DATABASE_URL環境変数)" << std::endl;
            return 1;
        }

        if (!vm.count("api-key")) {
            std::cerr << "エラー: APIキーが必要です (--api-key または API_KEY環境変数)" << std::endl;
            return 1;
        }

        std::cout << "アプリケーション設定:" << std::endl;
        std::cout << "  データベースURL: " << vm["database-url"].as<std::string>() << std::endl;
        
        if (vm.count("admin-email")) {
            std::cout << "  管理者メール: " << vm["admin-email"].as<email_address>().addr << std::endl;
        }
        
        std::cout << "  最大ワーカー数: " << vm["max-workers"].as<int>() << std::endl;
        std::cout << "  デバッグモード: " << (vm["debug-mode"].as<bool>() ? "有効" : "無効") << std::endl;
        std::cout << "  APIキー: " << vm["api-key"].as<std::string>().substr(0, 8) << "..." << std::endl;

    } catch(po::validation_error& e) {
        std::cerr << "バリデーションエラー: 無効な値が入力されました" << std::endl;
        return 1;
    } catch(std::exception& e) {
        std::cerr << "エラー: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

サンプル設定ファイル (config.ini)

# サーバー設定
host = 0.0.0.0
port = 3000
timeout = 60
max-connections = 200

# ログ設定
log-level = debug

# セキュリティ設定
ssl-enabled = true

CMakeでの使用例

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

# Boostライブラリを検索
find_package(Boost REQUIRED COMPONENTS program_options)

add_executable(my_app main.cpp)
target_link_libraries(my_app Boost::program_options)
target_compile_features(my_app PRIVATE cxx_std_11)