自定义验证函数以通过 Boost 程序选项解析 std::chrono::milliseconds
Custom validate function to parse std::chrono::milliseconds via Boost program options
我正在尝试通过 Boost 程序选项解析一个选项,其中包含 [s] 或 [ms] 中的时间。目前,变量被硬编码为,使用文字:
std::chrono::milliseconds timeout = 10s;
我很高兴在配置文件中这样定义它
#time in [s]
timeout = 10
但是我不知道如何执行验证功能。这是尝试过的:
struct chrono_ms : public std::chrono::milliseconds {};
void validate( boost::any& v,
const std::vector<std::string>& values,
chrono_ms*,
int)
{
// Make sure no previous assignment to 'v' was made.
validators::check_first_occurrence(v);
// Extract the first string from 'values'. If there is more than
// one string, it's an error, and exception will be thrown.
const std::string& s = validators::get_single_string(values);
// Convert to std::chrono::milliseconds.
v = std::chrono::milliseconds(std::stoi(s));
} // validate() for std::chrono::milliseconds
有趣的是,我设法为 std::array 编写了一个验证函数,但我对 std::chrono 不熟悉,而且我这辈子都想不出该怎么做...任何建议将不胜感激,谢谢。
除了问题(不清楚)之外,std::chrono
的重点是您不必了解单位。
只需将参数设为 std::chrono::duration<>
。
设计问题
首先,我怀疑您遇到的真正问题是您的 chrono_ms
不可构造。您需要继承一些构造函数,例如
using clock = std::chrono::steady_clock;
struct duration : public clock::duration {
using clock::duration::duration;
};
接下来,有一些问题源自标准库类型,而这些问题并非设计为源自。例如,5 * chrono_ms(1)
的类型不会是 chrono_ms
而是 std::chrono::milliseconds
.
还有隐式转换问题(由于继承显式构造函数)。
出于这个原因,我建议改用一个简单的包装器:
using clock = std::chrono::steady_clock;
struct duration {
clock::duration value;
};
这会让你明确地写下你的意思,不要有意外。
验证
接下来,这是我对采用单位的选项解析器的建议:
template<class charT>
void validate(boost::any& v, const std::vector< std::basic_string<charT> >& xs, duration*, long)
{
po::validators::check_first_occurrence(v);
std::basic_string<charT> s(po::validators::get_single_string(xs));
int magnitude;
clock::duration factor;
namespace qi = boost::spirit::qi;
qi::symbols<char, clock::duration> unit;
unit.add("s",1s)("ms",1ms)("us",1us)("µs",1us)("m",1min)("h",1h);
if (parse(s.begin(), s.end(), qi::int_ >> (unit|qi::attr(1s)) >> qi::eoi, magnitude, factor))
v = duration {magnitude * factor};
else
throw po::invalid_option_value(s);
}
您 不需要 将其放入 boost 或 program_options 命名空间。 ADL 会找到它(这可能是您想要像 chrono_ms
这样的 "strong" typedef 的全部原因)。
一个测试程序:
#include <boost/program_options.hpp>
#include <boost/spirit/include/qi.hpp>
#include <chrono>
#include <vector>
#include <iostream>
namespace po = boost::program_options;
using namespace std::chrono_literals;
namespace myns {
using clock = std::chrono::steady_clock;
struct duration {
clock::duration value;
friend std::ostream& operator<<(std::ostream& os, duration const& holder) {
using namespace std::chrono;
auto ms = duration_cast<milliseconds>(holder.value).count();
if (ms >= 1000)
return os << (ms/1000) << "s";
else
return os << ms << "ms";
}
};
template<class charT>
void validate(boost::any& v, const std::vector< std::basic_string<charT> >& xs, duration*, long)
{
po::validators::check_first_occurrence(v);
std::basic_string<charT> s(po::validators::get_single_string(xs));
int magnitude;
clock::duration factor;
namespace qi = boost::spirit::qi;
qi::symbols<char, clock::duration> unit;
unit.add("s",1s)("ms",1ms)("us",1us)("µs",1us)("m",1min)("h",1h);
if (parse(s.begin(), s.end(), qi::int_ >> unit >> qi::eoi, magnitude, factor))
v = duration {magnitude * factor};
else
throw po::invalid_option_value(s);
}
}
int main() {
po::options_description options;
options.add_options()
("duration,d", po::value<myns::duration>(), "duration (e.g. 1s or 10ms)");
char const* tests[][3] = {
{ "", "-d", "1s" },
{ "", "-d", "2200us" },
{ "", "-d", "10ms" },
{ "", "-d", "5m" },
{ "", "-d", "24h" },
//
{ "", "-d", "s" }, // invalid
{ "", "-d", "5" }, // invalid
};
for (auto args : tests) try {
std::copy(args, args +3, std::ostream_iterator<std::string>(std::cout << "Test ", " "));
auto parsed = po::parse_command_line(3, args, options);
po::variables_map vm;
po::store(parsed, vm);
po::notify(vm);
std::cout << "\tduration=" << vm["duration"].as<myns::duration>() << "\n";
} catch (std::exception const& e) {
std::cout << "\t" << e.what() << "\n";
}
}
版画
Test -d 1s duration=1s
Test -d 2200us duration=2ms
Test -d 10ms duration=10ms
Test -d 5m duration=300s
Test -d 24h duration=86400s
Test -d s Error 'the argument ('s') for option '--duration' is invalid'
Test -d 5 Error 'the argument ('5') for option '--duration' is invalid'
奖金
如果你,例如想要使某个单位成为默认单位,将解析器表达式中的 unit
替换为例如(unit|qi::attr(1s))
:
Test -d 1s duration=1s
Test -d 2200us duration=2ms
Test -d 10ms duration=10ms
Test -d 5m duration=300s
Test -d 24h duration=86400s
Test -d s the argument ('s') for option '--duration' is invalid
Test -d 5 duration=5s
我正在尝试通过 Boost 程序选项解析一个选项,其中包含 [s] 或 [ms] 中的时间。目前,变量被硬编码为,使用文字:
std::chrono::milliseconds timeout = 10s;
我很高兴在配置文件中这样定义它
#time in [s]
timeout = 10
但是我不知道如何执行验证功能。这是尝试过的:
struct chrono_ms : public std::chrono::milliseconds {};
void validate( boost::any& v,
const std::vector<std::string>& values,
chrono_ms*,
int)
{
// Make sure no previous assignment to 'v' was made.
validators::check_first_occurrence(v);
// Extract the first string from 'values'. If there is more than
// one string, it's an error, and exception will be thrown.
const std::string& s = validators::get_single_string(values);
// Convert to std::chrono::milliseconds.
v = std::chrono::milliseconds(std::stoi(s));
} // validate() for std::chrono::milliseconds
有趣的是,我设法为 std::array 编写了一个验证函数,但我对 std::chrono 不熟悉,而且我这辈子都想不出该怎么做...任何建议将不胜感激,谢谢。
除了问题(不清楚)之外,std::chrono
的重点是您不必了解单位。
只需将参数设为 std::chrono::duration<>
。
设计问题
首先,我怀疑您遇到的真正问题是您的 chrono_ms
不可构造。您需要继承一些构造函数,例如
using clock = std::chrono::steady_clock;
struct duration : public clock::duration {
using clock::duration::duration;
};
接下来,有一些问题源自标准库类型,而这些问题并非设计为源自。例如,5 * chrono_ms(1)
的类型不会是 chrono_ms
而是 std::chrono::milliseconds
.
还有隐式转换问题(由于继承显式构造函数)。
出于这个原因,我建议改用一个简单的包装器:
using clock = std::chrono::steady_clock;
struct duration {
clock::duration value;
};
这会让你明确地写下你的意思,不要有意外。
验证
接下来,这是我对采用单位的选项解析器的建议:
template<class charT>
void validate(boost::any& v, const std::vector< std::basic_string<charT> >& xs, duration*, long)
{
po::validators::check_first_occurrence(v);
std::basic_string<charT> s(po::validators::get_single_string(xs));
int magnitude;
clock::duration factor;
namespace qi = boost::spirit::qi;
qi::symbols<char, clock::duration> unit;
unit.add("s",1s)("ms",1ms)("us",1us)("µs",1us)("m",1min)("h",1h);
if (parse(s.begin(), s.end(), qi::int_ >> (unit|qi::attr(1s)) >> qi::eoi, magnitude, factor))
v = duration {magnitude * factor};
else
throw po::invalid_option_value(s);
}
您 不需要 将其放入 boost 或 program_options 命名空间。 ADL 会找到它(这可能是您想要像 chrono_ms
这样的 "strong" typedef 的全部原因)。
一个测试程序:
#include <boost/program_options.hpp>
#include <boost/spirit/include/qi.hpp>
#include <chrono>
#include <vector>
#include <iostream>
namespace po = boost::program_options;
using namespace std::chrono_literals;
namespace myns {
using clock = std::chrono::steady_clock;
struct duration {
clock::duration value;
friend std::ostream& operator<<(std::ostream& os, duration const& holder) {
using namespace std::chrono;
auto ms = duration_cast<milliseconds>(holder.value).count();
if (ms >= 1000)
return os << (ms/1000) << "s";
else
return os << ms << "ms";
}
};
template<class charT>
void validate(boost::any& v, const std::vector< std::basic_string<charT> >& xs, duration*, long)
{
po::validators::check_first_occurrence(v);
std::basic_string<charT> s(po::validators::get_single_string(xs));
int magnitude;
clock::duration factor;
namespace qi = boost::spirit::qi;
qi::symbols<char, clock::duration> unit;
unit.add("s",1s)("ms",1ms)("us",1us)("µs",1us)("m",1min)("h",1h);
if (parse(s.begin(), s.end(), qi::int_ >> unit >> qi::eoi, magnitude, factor))
v = duration {magnitude * factor};
else
throw po::invalid_option_value(s);
}
}
int main() {
po::options_description options;
options.add_options()
("duration,d", po::value<myns::duration>(), "duration (e.g. 1s or 10ms)");
char const* tests[][3] = {
{ "", "-d", "1s" },
{ "", "-d", "2200us" },
{ "", "-d", "10ms" },
{ "", "-d", "5m" },
{ "", "-d", "24h" },
//
{ "", "-d", "s" }, // invalid
{ "", "-d", "5" }, // invalid
};
for (auto args : tests) try {
std::copy(args, args +3, std::ostream_iterator<std::string>(std::cout << "Test ", " "));
auto parsed = po::parse_command_line(3, args, options);
po::variables_map vm;
po::store(parsed, vm);
po::notify(vm);
std::cout << "\tduration=" << vm["duration"].as<myns::duration>() << "\n";
} catch (std::exception const& e) {
std::cout << "\t" << e.what() << "\n";
}
}
版画
Test -d 1s duration=1s
Test -d 2200us duration=2ms
Test -d 10ms duration=10ms
Test -d 5m duration=300s
Test -d 24h duration=86400s
Test -d s Error 'the argument ('s') for option '--duration' is invalid'
Test -d 5 Error 'the argument ('5') for option '--duration' is invalid'
奖金
如果你,例如想要使某个单位成为默认单位,将解析器表达式中的 unit
替换为例如(unit|qi::attr(1s))
:
Test -d 1s duration=1s
Test -d 2200us duration=2ms
Test -d 10ms duration=10ms
Test -d 5m duration=300s
Test -d 24h duration=86400s
Test -d s the argument ('s') for option '--duration' is invalid
Test -d 5 duration=5s