[英]Input Stream Operator lookup order with Boost.Program_options
我在名称空间fw::example
有一个枚举类和一个对应的输入流运算符。
#include <stdexcept>
#include <string>
#include <istream>
namespace fw {
namespace example {
enum class Booleans {
kFalse = 0,
kTrue = 1
};
std::istream& operator>>(std::istream& is, Booleans& boolean) {
std::string token;
is >> token;
if ("true" == token) {
boolean = Booleans::kTrue;
} else if ("false" == token) {
boolean = Booleans::kFalse;
} else {
throw std::invalid_argument{
"Invalid string representation for an enumerator of the Booleans class."};
}
return is;
}
} // namespace example
} // namespace fw
然后,我通过Boost.Program_options在同一名称空间中绑定该枚举的变量:
// [...]
description_.add_options()(
"bool",
boost::program_options::value<Booleans>(&boolean_)
->required()
->default_value(Booleans::kFalse),
"A boolean string, either true or false.");
// [...]
但是我不想向用户公开std::runtime_error
,而是我想使用框架中的适当异常,即boost::program_options::invalid_option_value
。 想象一个更复杂的场景,其中在库中定义了枚举类和输入流运算符,而我无法修改该库。
因此,我尝试了以下操作(根据我在SO上找到的答案,我再也找不到了):
namespace boost {
namespace program_options {
std::istream& operator>>(std::istream& is,
fw::example::Booleans& boolean) {
try {
return fw::example::operator>>(is, boolean);
} catch (std::invalid_argument const& kException) {
throw invalid_option_value{kException.what()};
}
}
} // namespace program_options
} // namespace boost
整个代码可以编译,但事实是,仅调用了自由函数fw::example::operator>>
,因此传播了错误的执行:
terminate called after throwing an instance of 'std::invalid_argument'
what(): Invalid string representation for an enumerator of the Booleans class.
我正在寻找一种解决方案,以便能够将Boost.Program_options相关的代码与其余代码完全分开,而且还可以使用适当的异常。
我正在使用以下环境:
-std=c++11
参数的GCC v4.71。 问题似乎是您正在(使用)流运算符进行验证。
而是提供一个Custom Validator进行验证。
在这种情况下,您不再与operator <<的行为发生冲突。
在program_options
命名空间内添加竞争重载时,您可能希望的最佳选择是,编译器将找到可接受的重载/ too /,因此无法通过“歧义重载”进行编译
感谢用户@sehe,我使用标准的流运算符和Boost.Program_options的自定义验证器 ,提出了此答案中提出的解决方案。
忽略DRY问题(如提到的源注释中所述),该解决方案看起来很干净。 我还合并了https://stackoverflow.com/a/5517755/891439中描述的常见陷阱。
使用GCC v4.81和Boost.Program_options v1.56的源代码:
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>
#include "boost/any.hpp"
#include "boost/program_options.hpp"
// Imagine the source code in the following namespace block is placed in an
// external library.
// The mapping between enumerators and strings violates the DRY principle, but
// that issue can be easily fixed, e. g. by using one [Boost.Bimap][4].
namespace fw {
namespace example {
enum class Booleans : std::uint8_t {
kFalse = 0,
kTrue = 1
};
std::ostream& operator<<(std::ostream& os, Booleans const& kBoolean) {
switch (kBoolean) {
case Booleans::kFalse:
os << "false";
break;
case Booleans::kTrue:
os << "true";
break;
default:
os.setstate(std::ios_base::failbit);
break;
}
return os;
}
std::istream& operator>>(std::istream& is, Booleans& boolean) {
std::string token;
is >> token;
if ("true" == token) {
boolean = Booleans::kTrue;
} else if ("false" == token) {
boolean = Booleans::kFalse;
} else {
is.setstate(std::ios_base::failbit);
}
return is;
}
} // namespace example
} // namespace fw
// The following source code is application specific code.
namespace fw {
namespace example {
void validate(boost::any& value,
std::vector<std::string> const& kValues,
fw::example::Booleans*,
int) {
boost::program_options::validators::check_first_occurrence(value);
std::string const& kStringValue{
boost::program_options::validators::get_single_string(kValues)};
std::istringstream iss{kStringValue};
Booleans real_value;
iss >> real_value;
if (!iss) {
throw boost::program_options::validation_error{
boost::program_options::validation_error::invalid_option_value};
}
value = boost::any{real_value};
}
} // namespace example
} // namespace fw
int main(int argc, char** argv) {
fw::example::Booleans boolean;
boost::program_options::variables_map variables_map;
boost::program_options::options_description options_description{
"Allowed Options"};
options_description.add_options()
("help,h", "Display the help message.")
("bool",
boost::program_options::value<fw::example::Booleans>(
&boolean)->required(),
"Set the boolean value.");
try {
boost::program_options::store(
boost::program_options::parse_command_line(argc,
argv,
options_description),
variables_map);
if (variables_map.count("help")) {
std::cout << options_description << "\n";
return 1;
}
boost::program_options::notify(variables_map);
} catch (boost::program_options::error const &kException) {
std::cerr << "Program Options Error: "
<< kException.what()
<< "\n\nSpecify -help for further information.\n";
return 2;
}
if (variables_map.count("bool")) {
std::cout << "The boolean value was set to "
<< variables_map["bool"].as<fw::example::Booleans>()
<< ".\n";
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.