在 Boost 中处理配置文件中的无值选项 program_options
Handle no value option in Config file in Boost program_options
这个问题之前已经在这里问过 - Boost parse_config_file, empty key value。但由于那里没有提供合适的解决方案,我再次提出这个问题希望有人能提供更好的解决方案。
与上面的问题不同,在我的例子中,代码没有抛出 Boost 错误:
boost::program_options::invalid_option_value
相反,对于 string options
,它将值设置为 empty string("")
,对于 bool
,它设置为 true
。我的代码的目的是确定配置文件中是否设置了某些选项。我希望使用 vm[optionName].count()
(其中 vm 是 variables_map 对象)对此进行测试,但在未指定值的情况下,如 option=
,此 returns 为真,因此无法使用。
我也试过 - vm[optionName].defaulted()
, vm[optionName].empty()
和 implicit_value()
和 default_value()
添加选项但是 none 它们起作用了。
UPDATE 最初的回答理解错了。这是更新。
因此您希望 foo=
(没有值)的行为就好像该行甚至不在配置中一样。
这意味着默认值语义(即通知时发生的情况 - 将状态从解析器组件迁移到存储组件)不好。
您可以通过发明自己的值语义(mybool_switch,可以这么说)和/或解决 value<my_particulat_bool>
在其中添加流式操作,以便该选项的行为方式你要。也就是说,用大炮打苍蝇。
然而,到目前为止,更简单的选择是在解析器阶段进行干预,在 notify()
.
之前更改 parsed_options
这是一个带有现场演示的相当完整的插图:
#include <boost/program_options/config.hpp>
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
namespace po = boost::program_options;
int main() {
po::options_description desc;
desc.add_options()
("foo", po::bool_switch())
("bar", po::bool_switch()->default_value(false))
("qux", po::bool_switch()->implicit_value(false))
;
std::set<std::string> const bool_switches {"foo", "bar", "qux" };
for (std::string contents :
{ "", "foo=", "foo=true",
"bar=", "bar=true",
"qux=", "qux=true"})
{
std::istringstream iss(contents);
po::parsed_options parsed = po::parse_config_file(iss, desc, false);
std::cout << "\n---\n" << std::quoted(contents) << "\n";
// the magic is here:
for (auto it = parsed.options.begin(); it!= parsed.options.end();) {
using V = std::vector<std::string>;
V const& v = it->value;
if (bool_switches.count(it->string_key) && (v==V{} || v==V{""})) {
std::cout << "*** Discarding config key without a value: " << it->string_key << "\n";
it = parsed.options.erase(it);
} else {
++it;
}
}
po::variables_map vm;
po::store(parsed, vm);
for (auto& key : bool_switches) {
auto& entry = vm[key];
std::cout << " " << key << " ->" << std::boolalpha
<< (entry.empty()?" .empty()":"")
<< (entry.defaulted()?" .defaulted()":"");
if (entry.empty())
std::cout << " (no value)\n";
else
std::cout << " value:" << entry.as<bool>() << "\n";
}
}
}
哪个会打印
---
""
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"foo="
*** Discarding config key without a value: foo
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"foo=true"
bar -> .defaulted() value:false
foo -> value:true
qux -> .defaulted() value:false
---
"bar="
*** Discarding config key without a value: bar
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"bar=true"
bar -> value:true
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"qux="
*** Discarding config key without a value: qux
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"qux=true"
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> value:true
这个问题之前已经在这里问过 - Boost parse_config_file, empty key value。但由于那里没有提供合适的解决方案,我再次提出这个问题希望有人能提供更好的解决方案。
与上面的问题不同,在我的例子中,代码没有抛出 Boost 错误:
boost::program_options::invalid_option_value
相反,对于 string options
,它将值设置为 empty string("")
,对于 bool
,它设置为 true
。我的代码的目的是确定配置文件中是否设置了某些选项。我希望使用 vm[optionName].count()
(其中 vm 是 variables_map 对象)对此进行测试,但在未指定值的情况下,如 option=
,此 returns 为真,因此无法使用。
我也试过 - vm[optionName].defaulted()
, vm[optionName].empty()
和 implicit_value()
和 default_value()
添加选项但是 none 它们起作用了。
UPDATE 最初的回答理解错了。这是更新。
因此您希望 foo=
(没有值)的行为就好像该行甚至不在配置中一样。
这意味着默认值语义(即通知时发生的情况 - 将状态从解析器组件迁移到存储组件)不好。
您可以通过发明自己的值语义(mybool_switch,可以这么说)和/或解决 value<my_particulat_bool>
在其中添加流式操作,以便该选项的行为方式你要。也就是说,用大炮打苍蝇。
然而,到目前为止,更简单的选择是在解析器阶段进行干预,在 notify()
.
parsed_options
这是一个带有现场演示的相当完整的插图:
#include <boost/program_options/config.hpp>
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
namespace po = boost::program_options;
int main() {
po::options_description desc;
desc.add_options()
("foo", po::bool_switch())
("bar", po::bool_switch()->default_value(false))
("qux", po::bool_switch()->implicit_value(false))
;
std::set<std::string> const bool_switches {"foo", "bar", "qux" };
for (std::string contents :
{ "", "foo=", "foo=true",
"bar=", "bar=true",
"qux=", "qux=true"})
{
std::istringstream iss(contents);
po::parsed_options parsed = po::parse_config_file(iss, desc, false);
std::cout << "\n---\n" << std::quoted(contents) << "\n";
// the magic is here:
for (auto it = parsed.options.begin(); it!= parsed.options.end();) {
using V = std::vector<std::string>;
V const& v = it->value;
if (bool_switches.count(it->string_key) && (v==V{} || v==V{""})) {
std::cout << "*** Discarding config key without a value: " << it->string_key << "\n";
it = parsed.options.erase(it);
} else {
++it;
}
}
po::variables_map vm;
po::store(parsed, vm);
for (auto& key : bool_switches) {
auto& entry = vm[key];
std::cout << " " << key << " ->" << std::boolalpha
<< (entry.empty()?" .empty()":"")
<< (entry.defaulted()?" .defaulted()":"");
if (entry.empty())
std::cout << " (no value)\n";
else
std::cout << " value:" << entry.as<bool>() << "\n";
}
}
}
哪个会打印
---
""
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"foo="
*** Discarding config key without a value: foo
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"foo=true"
bar -> .defaulted() value:false
foo -> value:true
qux -> .defaulted() value:false
---
"bar="
*** Discarding config key without a value: bar
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"bar=true"
bar -> value:true
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"qux="
*** Discarding config key without a value: qux
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"qux=true"
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> value:true