如何结合boost::optional与->multitoken()及->composing()使用?
尝试使用Boost库的Program Options组件,通过->multitoken()和->composing()实现两种参数传递方式:一次指定多个参数(如--letter a b c),或多次指定同一标志(如--letter a --letter b),同时希望用boost::optional<>将该标志设为可选选项(即未指定时为boost::none,指定后包含对应值)。
未使用boost::optional包装时,代码功能符合预期:执行命令run //path/to/example:main --letter a b c会输出a b c;但将选项类型改为boost::optional<vector<string>>后,即:
("letter", po::value<boost::optional<vector<string>>>()->multitoken()->composing(), "multiple values");
执行--letter a b c会报错option '--letter' only takes a single argument,执行--letter a --letter b会报错option '--letter' cannot be specified more than once。
原代码示例:
/* The simplest usage of the library. */ #include <boost/optional.hpp> #include <boost/program_options.hpp> namespace po = boost::program_options; #include <iostream> #include <iterator> using namespace std; int main(int ac, char* av[]) { try { po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("letter", po::value<vector<string>>()->multitoken()->composing(), "multiple values"); po::variables_map vm; po::store(po::parse_command_line(ac, av, desc), vm); po::notify(vm); if (vm.count("help")) { cout << desc << "\n"; return 0; } if (vm.count("letter")) { vector<string> list = vm["letter"].as<vector<string>>(); for (size_t i = 0; i < list.size(); ++i) cout << list[i] << " "; cout << '\n'; } } catch (exception& e) { cerr << "error: " << e.what() << "\n"; return 1; } catch (...) { cerr << "Exception of unknown type!\n"; } return 0; }
核心原因
直接将boost::optional<vector<string>>作为选项值类型时,Boost Program Options的默认解析器无法正确适配multitoken()和composing():默认的optional<T>解析逻辑期望单个参数值,而multitoken()需要接收多个参数;同时composing()需要支持多次赋值合并,但optional<T>的默认赋值逻辑不支持追加元素到内部容器。
实现方案
无需将optional作为选项值的直接类型,而是保持选项值为vector<string>并保留multitoken()->composing(),在后续获取值时通过variables_map的count()方法判断选项是否存在,再用boost::optional包装结果。这种方式既满足了可选性需求,又保留了多参数/多次指定的功能,且无需给vector设置默认空值。
修改后的代码:
/* 使用boost::optional结合multitoken和composing的正确实现 */ #include <boost/optional.hpp> #include <boost/program_options.hpp> namespace po = boost::program_options; #include <iostream> #include <iterator> using namespace std; int main(int ac, char* av[]) { try { po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") // 保持选项值类型为vector<string>,保留multitoken和composing ("letter", po::value<vector<string>>()->multitoken()->composing(), "multiple values"); po::variables_map vm; po::store(po::parse_command_line(ac, av, desc), vm); po::notify(vm); if (vm.count("help")) { cout << desc << "\n"; return 0; } // 用boost::optional包装结果,通过vm.count判断是否存在该选项 boost::optional<vector<string>> letters; if (vm.count("letter")) { letters = vm["letter"].as<vector<string>>(); } // 使用optional的值 if (letters) { for (const auto& s : *letters) { cout << s << " "; } cout << '\n'; } else { cout << "No letters specified\n"; } } catch (exception& e) { cerr << "error: " << e.what() << "\n"; return 1; } catch (...) { cerr << "Exception of unknown type!\n"; } return 0; }
验证效果
- 执行
run //path/to/example:main --letter a b c,输出a b c - 执行
run //path/to/example:main --letter a --letter b,输出a b - 不指定
--letter时,letters为boost::none,输出No letters specified
内容的提问来源于stack exchange,提问作者Claire Lee

