C++と色々

主にC++やプログラムに関する記事を投稿します。

boost::program_options

boost::program_options名前空間にあるクラスを用いると、コマンドライン引数(オプション)を柔軟に簡単に解析できます。自作アプリケーションでオプションをつけるのならば使わない手はないです。
具体的には、渡されたコマンドライン引数をコマンド毎に(あれば)それぞれの引数をペアにして分け、オプションの順序に依存せず、省略可能オプションや引数を設定することができます。

let's boostさんのサンプルとほとんど一緒ですがサンプルコードを載せます。

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

int main(int argc, char** argv)
{
	namespace po = boost::program_options;
	po::options_description opt("オプション");
	opt.add_options()
		("help,h", "ヘルプを表示")
		("op", po::value<std::string>(), "演算子の種類(add, sub)")
		("lhs,l", po::value<int>(), "左辺値")
		("rhs,r", po::value<int>(), "右辺値");

options_descriptionクラスのコンストラクタに、オプション群の名称を設定します。
次にメンバ関数のadd_optionを使ってオプションを設定していきます。add_optionの第1引数にオプションの"--xxx"のxxxに当たる文字列を設定します。更にカンマ区切りでそのオプションの省略表現("-x"のx)を設定出来ます。無くても大丈夫です。第2引数に、そのオプションに引数がなければそのオプションの説明を、引数があればvalue()を渡します。その場合第3引数にオプションの設営を書くことになります。Tは受け取る引数の型です。

program_optionsクラスはoperator<<(std::ostream&, const program_options)がオーバーロードされているので、ストリームに流すことができます。コマンド一覧の文字列(ヘルプ)が渡されます。

         po::variables_map vm;
	try
	{
		po::store(po::parse_command_line(argc, argv, opt), vm);
	}
	catch(const po::error_with_option_name& e)
	{
		std::cout << e.what() << std::endl;
	}
	po::notify(vm);

variables_mapクラスはコマンド文字列をキーとし、そのオプションの引数を値としたmapです。値はどんな型でも大丈夫なようにboost::any型になっています。
parse_comman関数でコマンドライン引数をoptのオプション群に分解し、store関数でvariables_mapに代入していきます。
その際に、登録されていないオプションがコマンドライン引数に渡されたり、引数が必要なコマンドで引数がなかったり、必須のオプションがない場合、error_with_option_name例外が投げられます。
notify関数はvariables_mapのメンバ関数notifyを呼びます。

         if (vm.count("help") || !vm.count("op"))
	{
		std::cout << opt << std::endl;
	}
	else
	{
		try
		{
			const std::string op = vm["op"].as<std::string>();
			const int lhs = vm["lhs"].as<int>();
			const int rhs = vm["rhs"].as<int>();
			if (op == "add")
			{
				std::cout << lhs + rhs << std::endl;
			}
			if (op == "sub")
			{
				std::cout << lhs - rhs << std::endl;
			}
		}
		catch(const boost::bad_any_cast& e)
		{
			std::cout << e.what() << std::endl;
		}
	}

variables_mapのメンバ関数countはそのオプションが幾つ渡されたか返します。このコードの場合は、helpオプションが有るか、opオプションが無い場合はヘルプを表示するという処理になっています。オプションの引数はas関数で適切な型を指定して得られます。そのオプションに引数がないのに得ようとしたり、キャストが出来ない場合はbad_any_cast例外が投げられます。

ファイルからオプションを読み取る機能や、同じオプションに複数の値を指定することもできますが、今回はそれらの方法は省略します。

最後にサンプルコード全体載せます。

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

int main(int argc, char** argv)
{
	namespace po = boost::program_options;
	po::options_description opt("オプション");
	opt.add_options()
		("help,h", "ヘルプを表示")
		("op", po::value<std::string>(), "演算子の種類(add, sub)")
		("lhs,l", po::value<int>(), "左辺値")
		("rhs,r", po::value<int>(), "右辺値");
	po::variables_map vm;
	try
	{
		po::store(po::parse_command_line(argc, argv, opt), vm);
	}
	catch(const boost::program_options::error_with_option_name& e)
	{
		std::cout << e.what() << std::endl;
	}
	po::notify(vm);

	if (vm.count("help") || !vm.count("op"))
	{
		std::cout << opt << std::endl;
	}
	else
	{
		try
		{
			const std::string op = vm["op"].as<std::string>();
			const int lhs = vm["lhs"].as<int>();
			const int rhs = vm["rhs"].as<int>();
			if (op == "add")
			{
				std::cout << lhs + rhs << std::endl;
			}
			if (op == "sub")
			{
				std::cout << lhs - rhs << std::endl;
			}
		}
		catch(const boost::bad_any_cast& e)
		{
			std::cout << e.what() << std::endl;
		}
	}
}