I'm trying to parse a time string using boost spirit and not sure why this doesn't work.
auto fill_ts_nanos = [&t] (int h, int m, int s, int ms) -> int
{ t.tv_nsec = ( ( h * 3600 + m * 60 + s ) * 1000 + ms ) * 1000000; return t.tv_sec; };
auto fill_suffix = [&suffix] (string &s) { suffix=s; };
auto parse_ok = qi::parse(input.begin(), input.end(),
( qi::int_ >> qi::char_(":") >> qi::int_ >> qi::char_(":") >>
qi::int_ >> qi::char_(".") >> qi::int_ )
[boost::bind(fill_ts_nanos, qi::_1, qi::_3, qi::_5, qi::_7
>> qi::char_(",") >> qi::as_string[*qi::char_][fill_suffix] ;
A sample input is "04:00:00.512,2251812698588658"
After guessing a lot of details (e.g. what the type of t
is supposed to be), here's the fixed code with some debug output:
Note: I fixed the signed-ness of the numbers as well as I changed the types to prevent overflow.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <ctime>
#include <chrono>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
using namespace std::chrono_literals;
using namespace qi::labels;
int main() {
timespec t;
std::string suffix;
auto fill_ts_nanos = [&t](int h, unsigned m, unsigned s, unsigned ms) -> long {
t = {};
t.tv_nsec = ((h * 3600 + m * 60 + s) * 1000 + ms) * 1000000l;
return t.tv_sec;
};
auto fill_suffix = [&suffix](std::string &s) { suffix = s; };
std::string const input = "04:00:00.512,2251812698588658";
auto parse_ok = qi::parse(input.begin(), input.end(),
(qi::int_ >> ':' >> qi::uint_ >> ':' >> qi::uint_ >> '.' >> qi::uint_)
[px::bind(fill_ts_nanos, _1, _2, _3, _4) ]
>> ',' >> qi::as_string[*qi::char_]
[fill_suffix] );
std::printf("%lld.%.9ld\n", (long long)t.tv_sec, t.tv_nsec);
auto ns = t.tv_nsec * 1ns;
std::cout << std::fixed << std::setprecision(6);
std::cout << "hours: " << (ns / 1.0h) << "\n";
std::cout << "minutes: " << (ns / 1.0min) << "\n";
std::cout << "seconds: " << (ns / 1.0s) << "\n";
std::cout << "suffix: " << suffix << "\n";
return parse_ok? 0:255;
}
Prints
0.14400512000000
hours: 4.000142
minutes: 240.008533
seconds: 14400.512000
suffix: 2251812698588658
I'd try to simplify this by a lot, e.g., by creating a rule:
qi::rule<It, long()> timespec_ =
(qi::int_ >> ':' >> qi::uint_ >> ':' >> qi::uint_ >> '.' >> qi::uint_)
[ _val = ((_1 * 3600 + _2 * 60 + _3) * 1000 + _4) * 1000000l ];
Which means you can then parse with nothing else but:
timespec t {};
std::string suffix;
It f = input.begin(), l = input.end();
parse(f, l, timespec_ >> ',' >> *qi::char_, t.tv_nsec, suffix);
This has the same output:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <ctime>
#include <chrono>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
using namespace std::chrono_literals;
using namespace qi::labels;
using It = std::string::const_iterator;
qi::rule<It, long()> timespec_ =
(qi::int_ >> ':' >> qi::uint_ >> ':' >> qi::uint_ >> '.' >> qi::uint_)
[ _val = ((_1 * 3600 + _2 * 60 + _3) * 1000 + _4) * 1000000l ];
int main() {
std::string const input = "04:00:00.512,2251812698588658";
timespec t {};
std::string suffix;
It f = input.begin(), l = input.end();
if (parse(f, l, timespec_ >> ',' >> *qi::char_, t.tv_nsec, suffix)) {
std::printf("%lld.%.9ld\n", (long long)t.tv_sec, t.tv_nsec);
auto ns = t.tv_nsec * 1ns;
std::cout << std::fixed << std::setprecision(6);
std::cout << "hours: " << (ns / 1.0h) << "\n";
std::cout << "minutes: " << (ns / 1.0min) << "\n";
std::cout << "seconds: " << (ns / 1.0s) << "\n";
std::cout << "suffix: " << suffix << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f!=l) {
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
}