显然,向std
命名空间添加(几乎(任何内容都是未定义的行为。
我使用的是没有std::chrono::parse()
的C++14(仅C++20(,但我需要从istream
中取消std::chrono::milliseconds
类型(std::chrono::duration
的专业化(的值。
虽然这是有效的,但我找不到任何允许它不是UB的例外:
namespace std {
std::istream& operator >>(std::istream & is, std::chrono::milliseconds & ms) {
std::string s;
is >> s;
ms = std::chrono::milliseconds(std::stoi(s));
return is;
}
}
由于这两种参数类型都不是my类型,我不确定如何在std
命名空间之外安全地定义此运算符。
请注意,该运算符将在Boost::program_options
中被调用,因此我不认为我可以在自己的命名空间中定义该运算符,然后使用using my_ns::operator>>
,因为using
声明的作用域不会扩展到program_options
作用域。
我是如何来到这里的
作为Boost::program_options
的用户,我有一个特定的配置变量,我从存储为std::chrono::milliseconds
值的配置文件中读取:
std::chrono::milliseconds period;
po::options_description config_only_opts;
config_only_opts.add_options()
("control.period", po::value<std::chrono::milliseconds>(&period), "Specify the period in milliseconds");
// ...
auto istream = ifstream("config.cfg");
po::variables_map vm;
po::store(po::parse_config_file(istream, config_file_opts, false), vm);
根据Boost::program_options
文档,对于定义了operator >> (istream &, ...)
函数的类型,可以从配置文件中取消序列化值。
如果不扩展到上面提到的std
,我最终会出现这种编译器错误:
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp: In instantiation of ‘struct boost::detail::deduce_target_char_impl<boost::detail::deduce_character_type_later<std::chrono::duration<long int, std::ratio<1, 1000> > > >’:
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp:270:89: required from ‘struct boost::detail::deduce_target_char<std::chrono::duration<long int, std::ratio<1, 1000> > >’
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp:407:92: required from ‘struct boost::detail::lexical_cast_stream_traits<std::__cxx11::basic_string<char>, std::chrono::duration<long int, std::ratio<1, 1000> > >’
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp:468:15: required from ‘struct boost::detail::lexical_converter_impl<std::chrono::duration<long int, std::ratio<1, 1000> >, std::__cxx11::basic_string<char> >’
/home/david/opt/boost-1.69/include/boost/lexical_cast/try_lexical_convert.hpp:201:44: required from ‘bool boost::conversion::detail::try_lexical_convert(const Source&, Target&) [with Target = std::chrono::duration<long int, std::ratio<1, 1000> >; Source = std::__cxx11::basic_string<char>]’
/home/david/opt/boost-1.69/include/boost/lexical_cast.hpp:41:60: required from ‘Target boost::lexical_cast(const Source&) [with Target = std::chrono::duration<long int, std::ratio<1, 1000> >; Source = std::__cxx11::basic_string<char>]’
/home/david/opt/boost-1.69/include/boost/program_options/detail/value_semantic.hpp:92:36: required from ‘void boost::program_options::validate(boost::any&, const std::vector<std::__cxx11::basic_string<charT> >&, T*, long int) [with T = std::chrono::duration<long int, std::ratio<1, 1000> >; charT = char]’
/home/david/opt/boost-1.69/include/boost/program_options/detail/value_semantic.hpp:184:21: required from ‘void boost::program_options::typed_value<T, charT>::xparse(boost::any&, const std::vector<std::__cxx11::basic_string<charT> >&) const [with T = std::chrono::duration<long int, std::ratio<1, 1000> >; charT = char]’
/home/david/myproj/Config.cpp:208:1: required from here
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp:243:13: error: static assertion failed: Target type is neither std::istream`able nor std::wistream`able
BOOST_STATIC_ASSERT_MSG((result_t::value || boost::has_right_shift<std::basic_istream<wchar_t>, T >::value),
^
即使标准允许运算符重载,我也不建议添加该函数。这不是从std::istream
读取std::chrono::milliseconds
的通用方法。您所拥有的是应用程序自己从std::istream
中读取这样一个对象的方式。
我建议在应用程序自己的namespace
中添加一个函数。
namespace MyApp
{
std::istream& readChronoMilliSeconds(std::istream& is, std::chrono::milliseconds & ms)
{
std::string s;
is >> s;
ms = std::chrono::milliseconds(std::stoi(s));
return is;
}
}
为输入参数使用花哨的类型是在自欺欺人,因为输入参数通常只不过是数字、字符串或它们的列表。
我建议取一个合适的整数值,并在内部转换为毫秒。
int period_in_ms = 0;
...
config_only_opts.add_options()
("control.period_in_ms", po::value<int>(&period_in_ms), "Specify the period in milliseconds");
...
auto period = std::chrono::milliseconds(period_in_ms);
您可以使用Boost.ProgramOptions中的"通知"机制来更新period
的值,但这不值得麻烦。
如果您想以某种格式输入参数(例如,以"ms"结束,如"100ms"(,则使用value<std::string>
并进行转换/解析。