Boost Spirit X3 eol unexpected behaviour - c++

This question in strictly related to boost-spirit-x3-parse-into-structs
I have this grammar
#include <iostream>
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
struct sectionInfo
{
std::string name;
int number = 0;
float pitch = 0.0f;
int visible = 0;
float minCutsTblSize = 0.0f;
//technology section attributes
float gridResolution = 0.0f;
float lengthPrecision = 0.0f;
};
const char START_SECTION = '{';
const char END_SECTION = '}';
const char QUOTE = '"';
const char EQUALS = '=';
const char* LAYER_SECTION = "Layer";
const char* TECHNOLOGY_SECTION = "Technology";
const char* NUMBER_ATTR = "layerNumber";
const char* VISIBLE_ATTR = "visible";
const char* COLOR_ATTR = "color";
const char* PITCH_ATTR = "pitch";
const char* MIN_CUTS_TBL_SIZE_ATTR = "minCutsTblSize";
const char* GRID_RESOLUTION_ATTR = "gridResolution";
const char* LENGTH_PRECISION_ATTR = "lengthPrecision";
namespace Parser {
namespace x3 = boost::spirit::x3;
namespace detail {
template <typename T> auto propagate(T member) {
return [=](auto& ctx) { x3::traits::move_to(x3::_attr(ctx), x3::_val(ctx).*member); };
}
template <typename T = sectionInfo, typename P>
auto rule(const char* debug, P p) { return x3::rule<struct _, T> {debug} = x3::skip(x3::space)[p]; };
auto quoted = rule<std::string>("quoted", x3::lexeme[QUOTE >> +(x3::char_ - QUOTE) >> QUOTE]);
template <typename T> auto make_member_parser(bool T::* const member) { return x3::bool_[propagate(member)]; }
template <typename T> auto make_member_parser(int T::* const member) { return x3::int_[propagate(member)]; }
template <typename T> auto make_member_parser(double T::* const member) { return x3::double_[propagate(member)]; }
template <typename T> auto make_member_parser(float T::* const member) { return x3::double_[propagate(member)]; }
template <typename T> auto make_member_parser(std::string T::* const member) { return quoted[propagate(member)]; }
auto property = [](auto label, auto member) {
return x3::as_parser(label) >> EQUALS >> make_member_parser(member);
};
}
using detail::rule;
using detail::propagate;
using detail::property;
using detail::quoted;
auto number = property(NUMBER_ATTR, &sectionInfo::number);
auto visible = property(VISIBLE_ATTR, &sectionInfo::visible);
auto pitch = property(PITCH_ATTR, &sectionInfo::pitch);
auto minCutsTblSize = property(MIN_CUTS_TBL_SIZE_ATTR, &sectionInfo::minCutsTblSize);
auto lengthPrecision = property(LENGTH_PRECISION_ATTR, &sectionInfo::lengthPrecision);
auto gridResolution = property(GRID_RESOLUTION_ATTR, &sectionInfo::gridResolution);
auto skipLine = *(x3::char_ - x3::eol);
x3::rule<struct sectionInfoId, sectionInfo> const layer = "layer";
x3::rule<struct mainRuleId, std::vector<sectionInfo>> const mainRule = "mainRule";
auto layer_def =
LAYER_SECTION >> quoted[propagate(&sectionInfo::name)] >> START_SECTION >> x3::eol
>> *( (number | visible | pitch | minCutsTblSize | lengthPrecision | gridResolution) >> +x3::eol )
>> END_SECTION;
auto skipper = x3::blank;
auto mainRule_def = *(*x3::eol >> layer >> *x3::eol);
BOOST_SPIRIT_DEFINE(layer, mainRule);
}
std::ostream& operator<<(std::ostream& os, const sectionInfo& s)
{
os
<< "name=" << " " << s.name << "\n"
<< "number=" << " " << s.number << "\n"
<< "visible=" << " " << s.visible << "\n"
<< "pitch=" << " " << s.pitch << "\n"
<< "minCutsTblSize=" << " " << s.minCutsTblSize << "\n"
<< "lengthPrecision=" << " " << s.lengthPrecision << "\n"
<< "gridResolution=" << " " << s.gridResolution << "\n\n";
return os;
}
int main() {
std::stringstream ss;
ss
<<"\r\nLayer \"UBMB\" {\r\n"
<< " layerNumber = 170\r\n"
<< " pitch = 33.6\r\n"
<< "}\r\n"
<< "\r\n"
<< "Layer \"RV\" {\r\n"
<< " gridResolution = 0.34\r\n"
<< " minCutsTblSize = 22.7\r\n"
<< " layerNumber = 85\r\n"
<< " visible = 2\r\n"
<< " pitch = 331\r\n"
<< "}\r\n"
<< " \r\n"
<< "Layer \"foffo\" {\r\n"
<< " layerNumber = 125\r\n"
<< " pitch = 0.005\r\n"
<< " gridResolution = 21.7\r\n"
<< " lengthPrecision = 0.15\r\n"
<< "}\r\n"
<< "\r\n";
std::vector<sectionInfo> sections;
auto sample = ss.str();
auto f = sample.begin(), l = sample.end();
bool ok = boost::spirit::x3::phrase_parse(
f, l,
Parser::mainRule,
Parser::skipper,
sections
);
if (ok && f==l)
{
std::cout << "\n\n Parsed successfully \n\n";
for(auto& s : sections)
{
std::cout << s;
}
}
else
std::cout << "Parse failed\n";
}
which successfully parses the input:
output is:
name= UBMB
number= 170
visible= 0
pitch= 33.6
minCutsTblSize= 0
lengthPrecision= 0
gridResolution= 0
name= RV
number= 85
visible= 2
pitch= 331
minCutsTblSize= 22.7
lengthPrecision= 0
gridResolution= 0.34
name= foffo
number= 125
visible= 0
pitch= 0.005
minCutsTblSize= 0
lengthPrecision= 0.15
gridResolution= 21.7
The problem arises because I need to skip some lines, i.e. the lines with properties not of interest (not defined in my grammar)
edit: For example, there may be a property dummy = "foo" which I want to skip.
To achieve this, the layer rule
auto layer_def = LAYER_SECTION >> quoted[propagate(&sectionInfo::name)] >> START_SECTION >> x3::eol
>> *( (number | visible | pitch | minCutsTblSize | lengthPrecision | gridResolution ) >> +x3::eol )
>> END_SECTION;
becomes
auto layer_def = LAYER_SECTION >> quoted[propagate(&sectionInfo::name)] >> START_SECTION >> x3::eol
>> *( (number | visible | pitch | minCutsTblSize | lengthPrecision | gridResolution | skipLine) >> +x3::eol )
>> END_SECTION;
The parser succeeds, but the output is now
name= UBMB number= 125 visible= 2 pitch= 0.005 minCutsTblSize= 22.7
lengthPrecision= 0.15 gridResolution= 21.7
which is wrong (only one section, properties taken here and there...)
It is evident the problem resides in the skipLine rule
auto skipLine = *(x3::char_ - x3::eol);
and I can't figure out why.
I thought it was obvious that the rule *(char - eol) >> eol
would match any line, but I guess it isn't..
Any clues?

Firstly, I'd simplify skipping. The skipper doesn't belong in the call-site because it's important to the grammar. Encapsulate it into the mainRule, and simplify:
auto mainRule_def = x3::skip(x3::blank) [ -layer % x3::eol ];
The -layer expression is a trick that accepts empty lines. Now using the parser becomes:
bool ok = boost::spirit::x3::parse(f, l, Parser::mainRule, sections);
Next: skipLine eats '}' as well, making everything fall apart: everything after that is taken as (invalid) properties for the same layer, and finally there is no END_SECTION so the grammar fails to match.
Simply:
auto skipLine = *(x3::char_ - x3::eol - END_SECTION);
Pro Tip:
When in doubt, debug: Live On Coliru
// debug it
auto skipLine = x3::rule<struct skipLine_> {"skipLine"} = *(x3::char_ - x3::eol/* - END_SECTION*/);
Full Demo
With some minor simplifications
Live On Coliru
#include <iostream>
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>
struct sectionInfo {
std::string name;
int number = 0;
float pitch = 0.0f;
int visible = 0;
float minCutsTblSize = 0.0f;
// technology section attributes
float gridResolution = 0.0f;
float lengthPrecision = 0.0f;
};
char const START_SECTION = '{';
char const END_SECTION = '}';
char const QUOTE = '"';
char const EQUALS = '=';
char const* LAYER_SECTION = "Layer";
char const* TECHNOLOGY_SECTION = "Technology";
char const* NUMBER_ATTR = "layerNumber";
char const* VISIBLE_ATTR = "visible";
char const* COLOR_ATTR = "color";
char const* PITCH_ATTR = "pitch";
char const* MIN_CUTS_TBL_SIZE_ATTR = "minCutsTblSize";
char const* GRID_RESOLUTION_ATTR = "gridResolution";
char const* LENGTH_PRECISION_ATTR = "lengthPrecision";
namespace Parser {
namespace x3 = boost::spirit::x3;
namespace detail {
template <typename T> auto propagate(T member) {
return [=](auto &ctx) { x3::traits::move_to(x3::_attr(ctx), x3::_val(ctx).*member); };
}
template <typename T = sectionInfo, typename P> auto rule(const char *debug, P p) {
return x3::rule<struct _, T>{ debug } = x3::skip(x3::space)[p];
};
auto quoted = rule<std::string>("quoted", x3::lexeme[QUOTE >> +(x3::char_ - QUOTE) >> QUOTE]);
#define MMP_(T, p) template <typename U> auto make_member_parser(T U::*const member) { return (p)[propagate(member)]; }
MMP_(bool, x3::bool_);
MMP_(int, x3::int_);
MMP_(double, x3::double_);
MMP_(float, x3::double_);
MMP_(std::string, quoted);
#undef MMP_
auto property = [](auto label, auto member) { return x3::as_parser(label) >> EQUALS >> make_member_parser(member); };
}
using detail::rule;
using detail::propagate;
using detail::property;
using detail::quoted;
auto number = property(NUMBER_ATTR, &sectionInfo::number);
auto visible = property(VISIBLE_ATTR, &sectionInfo::visible);
auto pitch = property(PITCH_ATTR, &sectionInfo::pitch);
auto minCutsTblSize = property(MIN_CUTS_TBL_SIZE_ATTR, &sectionInfo::minCutsTblSize);
auto lengthPrecision = property(LENGTH_PRECISION_ATTR, &sectionInfo::lengthPrecision);
auto gridResolution = property(GRID_RESOLUTION_ATTR, &sectionInfo::gridResolution);
auto skipLine = *(x3::char_ - x3::eol - END_SECTION);
x3::rule<struct sectionInfoId, sectionInfo> const layer = "layer";
x3::rule<struct mainRuleId, std::vector<sectionInfo> > const mainRule = "mainRule";
auto layer_def =
LAYER_SECTION >> quoted[propagate(&sectionInfo::name)]
>> START_SECTION >> x3::eol
>> *((number | visible | pitch | minCutsTblSize | lengthPrecision | gridResolution | skipLine) >> +x3::eol)
>> END_SECTION
;
auto mainRule_def = x3::skip(x3::blank) [ -layer % x3::eol ];
BOOST_SPIRIT_DEFINE(layer, mainRule);
}
std::ostream &operator<<(std::ostream &os, const sectionInfo &s) {
return os
<< "name=" << " " << s.name << "\n"
<< "number=" << " " << s.number << "\n"
<< "visible=" << " " << s.visible << "\n"
<< "pitch=" << " " << s.pitch << "\n"
<< "minCutsTblSize=" << " " << s.minCutsTblSize << "\n"
<< "lengthPrecision=" << " " << s.lengthPrecision << "\n"
<< "gridResolution=" << " " << s.gridResolution << "\n\n";
}
int main() {
std::string const sample =
"\r\nLayer \"UBMB\" {\r\n"
" layerNumber = 170\r\n"
" pitch = 33.6\r\n"
"}\r\n"
"\r\n"
"Layer \"RV\" {\r\n"
" gridResolution = 0.34\r\n"
" minCutsTblSize = 22.7\r\n"
" layerNumber = 85\r\n"
" visible = 2\r\n"
" pitch = 331\r\n"
"}\r\n"
" \r\n"
"Layer \"foffo\" {\r\n"
" layerNumber = 125\r\n"
" pitch = 0.005\r\n"
" gridResolution = 21.7\r\n"
" lengthPrecision = 0.15\r\n"
"}\r\n"
"\r\n";
std::vector<sectionInfo> sections;
{
auto f = sample.begin(), l = sample.end();
bool ok = boost::spirit::x3::parse(f, l, Parser::mainRule, sections);
if (ok && f == l) {
std::cout << "\n\n Parsed successfully \n\n";
for (auto &s : sections) {
std::cout << s;
}
} else
std::cout << "Parse failed\n";
}
}
Prints
Parsed successfully
name= UBMB
number= 170
visible= 0
pitch= 33.6
minCutsTblSize= 0
lengthPrecision= 0
gridResolution= 0
name= RV
number= 85
visible= 2
pitch= 331
minCutsTblSize= 22.7
lengthPrecision= 0
gridResolution= 0.34
name= foffo
number= 125
visible= 0
pitch= 0.005
minCutsTblSize= 0
lengthPrecision= 0.15
gridResolution= 21.7

Related

Semantic actions calling lambda

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:
Live On Coliru
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
Suggestions
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:
Live On Coliru
#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";
}
}
In my desperation I've also figured out how the container version works. See below,
auto test_fn = [&t](auto c) {
t.tv_nsec = ( ( at_c<0>(c) * 3600 +
at_c<2>(c) * 60 +
at_c<4>(c) ) * 1000 +
at_c<6>(c) ) * 1000000;
auto parse_ok = qi::parse(input.begin(), input.end(),
( qi::int_ >> qi::char_(":") >> qi::int_ >> qi::char_(":") >>
qi::int_ >> qi::char_(".") >> qi::int_ )[ test_fn ]
Admittedly, it's quite ugly.

Parsing list of 'key = value' with unknown keys with Boost::Spirit

I've a string like the following one:
[GENERAL]
FMax Antenna = 3000
FMin Antenna = 2000
Invalid key = Invalid value
EMin Antenna = -50
EMax Antenna = 80
I want to parse it in order to save the value of FMin Antenna, FMax Antenna, EMin Antenna, EMax Antenna in a structure. I've created a Spirit parser, but it works partially.
Since the file can have many key = value rows, I need to parse only what I need (the key values that I must read) ignoring other pairs.
Both key and value can be alphanumeric strings with spaces and tabs.
I've defined inside the parser the keys that I want to read but when I encounter an unknown key, I cannot read keys that follows it (in the example case, I can't read EMin Antenna and EMax Antenna because are defined after an unknown key).
I've tried the code below: If I parse file1, that contains only keys that I want to read, it works, but if I add unknown key = value pairs in the middle of the file, like in file2, it stops to read all subsequent lines.
How can I fix it and continue to parse the file after unknown key-value pairs?
#include <boost/optional/optional_io.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
const std::string file1 = R"xx(
[GENERAL]
FMax Antenna = 3000
FMin Antenna = 2000
EMin Antenna = -50
EMax Antenna = 80
)xx";
const std::string file2 = R"xx(
[GENERAL]
FMax Antenna = 3000
FMin Antenna = 2000
EMin Antenna = -50
pappa pio = po po
EMax Antenna = 80
Ciao = 55
)xx";
struct Data {
double minFrequency = 0.0;
double maxFrequency = 0.0;
double minElevation = 0.0;
double maxElevation = 0.0;
};
BOOST_FUSION_ADAPT_STRUCT(
Data,
(double, minFrequency)
(double, maxFrequency)
(double, minElevation)
(double, maxElevation)
)
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, Data(), Skipper> {
grammar() : grammar::base_type(start) {
auto minFrequency = bind(&Data::minFrequency, qi::_val);
auto maxFrequency = bind(&Data::maxFrequency, qi::_val);
auto minElevation = bind(&Data::minElevation, qi::_val);
auto maxElevation = bind(&Data::maxElevation, qi::_val);
start = qi::no_case["[GENERAL]"] >> *(
("FMin Antenna" >> qi::lit('=') >> qi::int_)[minFrequency = qi::_1] |
("FMax Antenna" >> qi::lit('=') >> qi::int_)[maxFrequency = qi::_1] |
("EMin Antenna" >> qi::lit('=') >> qi::int_)[minElevation = qi::_1] |
("EMax Antenna" >> qi::lit('=') >> qi::int_)[maxElevation = qi::_1] |
(+(qi::alnum | qi::blank) >> qi::lit('=') >> +(qi::alnum | qi::blank)) // Issue here?
);
}
private:
qi::rule<It, Data(), Skipper> start;
};
int main() {
using It = std::string::const_iterator;
Data parsed1, parsed2;
bool ok = qi::phrase_parse(file1.begin(), file1.end(), grammar<It>(), qi::space, parsed1);
std::cout << "--- File 1 ---" << std::endl;
std::cout << "parsed = " << std::boolalpha << ok << std::endl;
std::cout << "min freq = " << parsed1.minFrequency << std::endl;
std::cout << "max freq = " << parsed1.maxFrequency << std::endl;
std::cout << "min elev = " << parsed1.minElevation << std::endl;
std::cout << "max elev = " << parsed1.maxElevation << std::endl;
std::cout << "--- File 2 ---" << std::endl;
ok = qi::phrase_parse(file2.begin(), file2.end(), grammar<It>(), qi::space, parsed2);
std::cout << "parsed = " << std::boolalpha << ok << std::endl;
std::cout << "min freq = " << parsed2.minFrequency << std::endl;
std::cout << "max freq = " << parsed2.maxFrequency << std::endl;
std::cout << "min elev = " << parsed2.minElevation << std::endl;
std::cout << "max elev = " << parsed2.maxElevation << std::endl;
return 0;
}
Ouput:
--- File 1 ---
parsed = true
min freq = 2000
max freq = 3000
min elev = -50
max elev = 80
--- File 2 ---
parsed = true
min freq = 2000
max freq = 3000
min elev = -50
max elev = 0 <-- This should be 80 like in the first parsing
You're confused about skippers.
Newlines are significant in your grammar, which is why you need a skipper that doesn't eat them
In your rules, you match +(alnum|blank) which is never gonna work because the skipper eats everything that matches blank anyways
(See Boost spirit skipper issues for background)
Other notes:
You don't need Fusion adaptation unless you want auto-magic attribute propagation. You're not using it right now.
Solving It
I'd make things very explicit:
known =
("FMin Antenna" >> lit('=') >> int_)[minFrequency = _1] |
("FMax Antenna" >> lit('=') >> int_)[maxFrequency = _1] |
("EMin Antenna" >> lit('=') >> int_)[minElevation = _1] |
("EMax Antenna" >> lit('=') >> int_)[maxElevation = _1]
;
unknown = +alnum >> '=' >> +alnum;
setting = (known(_r1) | unknown) >> +eol;
start =
no_case["[GENERAL]"] >> eol
>> *setting(_val);
Splitting up in rules is a little tricky, because *setting would try to synthesize a container attribute, making it impossible to propagate to the actual Data attribute.
I solved it by passing the attribute by reference in an inherited attribute, which disables automatic attribute propagation.
Alternatively, you could add a semantic action of any kind to inhibit automatic attribute propagation
DEMO
Live On Coliru
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
struct Data {
double minFrequency = 0.0;
double maxFrequency = 0.0;
double minElevation = 0.0;
double maxElevation = 0.0;
};
template <typename It, typename Skipper = qi::blank_type>
struct grammar : qi::grammar<It, Data(), Skipper> {
grammar() : grammar::base_type(start) {
using namespace qi;
auto minFrequency = bind(&Data::minFrequency, _r1);
auto maxFrequency = bind(&Data::maxFrequency, _r1);
auto minElevation = bind(&Data::minElevation, _r1);
auto maxElevation = bind(&Data::maxElevation, _r1);
known =
("FMin Antenna" >> lit('=') >> int_)[minFrequency = _1] |
("FMax Antenna" >> lit('=') >> int_)[maxFrequency = _1] |
("EMin Antenna" >> lit('=') >> int_)[minElevation = _1] |
("EMax Antenna" >> lit('=') >> int_)[maxElevation = _1]
;
unknown = +alnum >> '=' >> +alnum;
setting = (known(_r1) | unknown) >> +eol;
start =
no_case["[GENERAL]"] >> eol
>> *setting(_val);
}
private:
qi::rule<It, Data(), Skipper> start;
qi::rule<It, void(Data&), Skipper> setting, known;
qi::rule<It, Skipper> unknown;
};
int main() {
using It = std::string::const_iterator;
grammar<It> const g;
for (std::string const file : {
"[GENERAL]\nFMax Antenna = 3000\nFMin Antenna = 2000\nEMin Antenna = -50\nEMax Antenna = 80\n",
"[GENERAL]\nFMax Antenna = 3000\nFMin Antenna = 2000\nEMin Antenna = -50\npappa pio = po po\nEMax Antenna = 80\nCiao = 55\n",
})
{
Data parsed;
It f = begin(file), l = end(file);
bool ok = qi::phrase_parse(f, l, g, qi::blank, parsed);
std::cout << "--- File ---" << "\n";
std::cout << "parsed = " << std::boolalpha << ok << "\n";
if (ok) {
std::cout << "min freq = " << parsed.minFrequency << "\n";
std::cout << "max freq = " << parsed.maxFrequency << "\n";
std::cout << "min elev = " << parsed.minElevation << "\n";
std::cout << "max elev = " << parsed.maxElevation << "\n";
}
if (f!=l) {
std::cout << "Remaining unparsed: ";
while (f!=l) {
char c = *f++;
if (isprint(c)) std::cout << c;
else std::cout << "\\x" << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(c);
}
}
}
}
Prints
--- File ---
parsed = true
min freq = 2000
max freq = 3000
min elev = -50
max elev = 80
--- File ---
parsed = true
min freq = 2000
max freq = 3000
min elev = -50
max elev = 80

Parsing CSV into Struct Array

I am trying to parse CSV into Array of structures. Delimiter is ';'. If you look into the code it seems that I filled only first element of the array out of nine that is 9 lines are in my CSV right now. Remaining elements of the array are just 0. Anyone got some advice? Thanks
fileTO.open("imput.csv", ios_base :: app);
fileFROM.open("imput.csv", ios_base :: app);
//IntoCsvThruConsole();
// array of structures from csv
string line;
string sID, stype, scategory, samount, sdate;
int lines = CountExistingLines();
Properties * structure = new Properties[lines];
int counter = 0;
int position = 0;
while (getline(fileFROM, line))
{
sID = "";
samount = "";
for (int i = 0; i < line.size(); i++)
{
if (line[i] == ';')
{
position++;
continue;
}
switch (position)
{
case 0 : sID = sID + line[i];
break;
case 1 : structure[counter].type = structure[counter].type + line[i];
break;
case 2 : structure[counter].category = structure[counter].category + line[i];
break;
case 3 : samount = samount + line[i];
break;
case 4 : structure[counter].date = structure[counter].date + line[i];
break;
}
}
structure[counter].ID = atoi(sID.c_str());
structure[counter].amount = atoi(samount.c_str());
cout << "ID zaznamu: " << structure[counter].ID << endl;
cout << "Typ: " << structure[counter].type << endl;
cout << "Kategorie: " << structure[counter].category << endl;
cout << "Castka: " << structure[counter].amount << endl;
cout << "Datum: " << structure[counter].date << endl;
counter++;
}
delete[] structure;
I have globally initialized struct correctly and also fstreams. Hope it is enough. Thanks
enter image description here
I recommend using Boost.Spirit for such parsing tasks.
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
struct Date
{
int day;
int month;
int year;
};
std::ostream& operator<<(std::ostream& os, Date const &d)
{
os << d.day << '/' << d.month << '/' << d.year;
return os;
}
BOOST_FUSION_ADAPT_STRUCT(
Date,
day, month, year)
struct Properties
{
int ID;
std::string type;
std::string category;
int amount;
Date date;
};
BOOST_FUSION_ADAPT_STRUCT(
Properties,
ID, type, category, amount, date)
std::vector<Properties> parse(std::string const &input)
{
auto iter = input.begin();
using namespace boost::spirit::x3;
auto name = rule<class name, std::string>{}
= lexeme[alpha >> *alpha];
auto date = rule<class date, Date>{}
= int_ > '/' > int_ > '/' > int_;
std::vector<Properties> result;
bool r = phrase_parse(iter, input.end(),
(int_ > ';' > name > ';' > name > ';' > int_ > ';' > date) % eol,
space - eol, result);
if (!r)
{
std::string rest(iter, input.end());
throw std::invalid_argument("Parsing failed at " + rest);
}
return result;
}
int main()
{
// This could be a file instead with std::ifstream
std::istringstream input;
input.str(
"1;TypeA;CategoryA;10;05/12/2017\n"
"2;TypeB;CategoryA;21;04/12/2017\n"
"3;TypeB;CategoryB;19;01/12/2017\n"
"4;TypeA;CategoryA; 6;20/11/2017\n");
std::vector<Properties> entry = parse(input.str());
for (auto e : entry)
{
std::cout << "Found the following entries:\n"
<< " ID: " << e.ID << "\n"
<< " Type: " << e.type << "\n"
<< " Category: " << e.category << "\n"
<< " Amount: " << e.amount << "\n"
<< " Date: " << e.date << "\n";
}
}
Live example

Convert vector<string> to other types

I've a class withmap<string,vector<string>>.
I want to give a user a member function to receive the value for a key in different formats than std::vector(string):
vector(string),
string,
vector(int),
vector(float) and
bool
example:
bool x = (bool) myClass["dummy_boolean_type"]
x = myClass["dummy_boolean_type"].as<bool>
int y = (int) myClass["dummy_boolean_type"]
Can someone have an example what is the best way to achieve it ?
(without a use in Boost)
You cannot provide your class with any member function or functions
that will support the two constructions you want:
T x = myClassInstance["key"].as<T> // (1)
T x = (T) myClassInstance["key"] // (2)
In the case of (1), the immediate reason is simply that the construction is
not legal C++. So let's suppose it is replaced by:
T x = myClassInstance["key"].as<T>() // (1a)
But this doesn't help, and the reason is the same for both (1a) and (2):
The expression myClassInstance["key"] will have to evaluate to some object e or reference to such that is returned by:
myClass::operator[](std::string const &);
This e is the vector<string> to which key maps in your
map<string,vector<string> data member of myClass. It is not myClassInstance.
So what you are asking for are member functions of myClass that will support
the constructions:
T x = e.as<T> // (1b)
T x = (T) e // (2b)
where e is an std::vector<std::string>.
Clearly, nothing you can do in myClass can address your requirement. In
(1b) and (2b), myClass is nowhere to be seen.
Is there any template member function of std::vector<std::string>:
template<typename T>
T as() const;
that has the behaviour you want for (1b)?
Does std::vector<std::string> have any template member function:
template<typename T>
operator T() const;
that has the behaviour you want for (2b)?
No and No.
However...
You can implement an as template member function of myClass that
supports conversions such as you mention. Its signature - of course -
would be:
template<typename T>
T myClass::as(std::string const & key) const;
and you would invoke it like:
T x = myClassInstance.as<T>("key") // (1c)
I'll sketch an implemention that assumes we're content with the
conversions from std::vector<std::string> to:
std::string
std::vector<int>
bool
which will be enough to get you under way.
To convert an std::vector<std::string> vs to std::string I'll concatenate
the elements of vs.
To convert an std::vector<std::string> vs to std::vector<int> I'll convert each element of vs from a decimal numeral, if I can, to the
integer the numeral represents, and return a vector of those integers. Without
prejudice to other policies I'll throw an std::invalid_argument exception
when an element doesn't trim to a decimal numeral.
I am spoilt for choice as to what you might mean by converting an std::vector<std::string>
to bool, so I will arbitrarily say that vs is true if I can convert it
to a vector<int> of which any element is non-0 and is false if I can convert
it to a vector<int> in which all elements are 0.
The sketch:
#include <type_traits>
#include <map>
#include <vector>
#include <string>
#include <algorithm>
namespace detail {
template<typename T>
struct convert_to
{
static T from(std::vector<std::string> const & vs) {
static constexpr bool always_false = !std::is_same<T,T>::value;
static_assert(always_false,
"Calling `convert_to<T>::from` for unimplemented `T`");
return *(T*)nullptr;
}
};
template<>
std::string convert_to<std::string>::from(std::vector<std::string> const & vs)
{
std::string s;
for ( auto const & e : vs ) {
s += e;
}
return s;
}
template<>
std::vector<int>
convert_to<std::vector<int>>::from(std::vector<std::string> const & vs)
{
auto lamb = [](std::string const & s) {
std::size_t lastoff = s.find_last_not_of(" \t\f\v\n\r");
int i;
try {
std::size_t nlen;
i = std::stoi(s,&nlen);
if (nlen <= lastoff) {
throw std::invalid_argument("");
}
}
catch(std::invalid_argument const & e) {
throw std::invalid_argument(
"Cannot convert \"" + s + "\" to int");
}
return i;
};
std::vector<int> vi;
std::transform(vs.begin(),vs.end(),std::back_inserter(vi),lamb);
return vi;
}
template<>
bool convert_to<bool>::from(std::vector<std::string> const & vs)
{
auto vi = convert_to<std::vector<int>>::from(vs);
for (auto const & i : vi) {
if (i) {
return true;
}
}
return false;
}
} // namespace detail
struct myClass // Your class
{
// Whatever...
std::vector<std::string> & operator[](std::string const & key) {
return _map[key];
}
template<typename T>
T as(std::string const & key) {
return detail::convert_to<T>::from(_map[key]);
}
// Whatever...
private:
std::map<std::string,std::vector<std::string>> _map;
};
The one take-away point here is the use of template<typename T>
detail::struct convert_to, with its solitary static member function:
T from(std::vector<std::string> const & vs)
which in the default instantiation will provoke a static_assert failure
reporting that no conversion to T from std::vector<std::string> has been
defined.
Then, for each type U to which you want a conversion, you have just to
write a specializing definition:
template<>
U convert_to<U>::from(std::vector<std::string> const & vs);
as you see fit, and the construction (1c) will use it as per:
template<typename T>
T myClass::as(std::string const & key) {
return detail::convert_to<T>::from(_map[key]);
}
Here's an illustrative progam you can append to the sketch:
#include <iostream>
using namespace std;
template<typename T>
static void print_vec(std::vector<T> const & v)
{
cout << "{ ";
for (auto const & e : v) {
cout << e << " ";
}
cout << "}\n";
}
static void print_vec(std::vector<std::string> const & v)
{
cout << "{ ";
for (auto const & e : v) {
cout << '\"' << e << "\" ";
}
cout << "}\n";
}
int main()
{
myClass f;
f["int_vec"] = vector<string>{"0","1 "," 2"};
cout << "f[\"int_vec\"] = "; print_vec(f["int_vec"]);
f["true_vec"] = vector<string>{"0"," 1 ","0"};
cout << "f[\"true_vec\"] = "; print_vec(f["true_vec"]);
f["false_vec"] = vector<string>{"0"," 0","0 "};
cout << "f[\"false_vec\"] = "; print_vec(f["false_vec"]);
f["not_int_vec0"] = vector<string>{"0","1","2",""};
cout << "f[\"not_int_vec0\"] = "; print_vec(f["not_int_vec0"]);
f["not_int_vec1"] = vector<string>{"0","#","2",};
cout << "f[\"not_int_vec1\"] = "; print_vec(f["not_int_vec1"]);
f["not_int_vec2"] = vector<string>{"0"," 1$","2",};
cout << "f[\"not_int_vec2\"] = "; print_vec(f["not_int_vec2"]);
cout << "f.as<string>(\"int_vec\") = \""
<< f.as<string>("int_vec") << '\"' << endl;
cout << "f.as<string>(\"true_vec\") = \""
<< f.as<string>("true_vec") << '\"' << endl;
cout << "f.as<string>(\"false_vec\") = \""
<< f.as<string>("false_vec") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec0\") = \""
<< f.as<string>("not_int_vec0") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec1\") = \""
<< f.as<string>("not_int_vec1") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec2\") = \""
<< f.as<string>("not_int_vec2") << '\"' << endl;
vector<int> va = f.as<vector<int>>("int_vec");
cout << "f.as<vector<int>>(\"int_vec\") = ";
print_vec(f.as<vector<int>>("int_vec"));
cout << boolalpha << "f.as<bool>(\"true_vec\") = "
<< f.as<bool>("true_vec") << endl;
cout << boolalpha << "f.as<bool>(\"false_vec\") = "
<< f.as<bool>("false_vec") << endl;
try {
cout << "f.as<vector<int>>(\"not_int_vec0\")...";
auto b = f.as<vector<int>>("not_int_vec0");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
try {
cout << "f.as<vector<int>>(\"not_int_vec1\")...";
auto b = f.as<vector<int>>("not_int_vec1");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
try {
cout << "f.as<vector<int>>(\"not_int_vec2\")...";
auto b = f.as<vector<int>>("not_int_vec2");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
// char ch = f.as<char>("int_vec"); <- static_assert fails
return 0;
}
It outputs:
f["int_vec"] = { "0" "1 " " 2" }
f["true_vec"] = { "0" " 1 " "0" }
f["false_vec"] = { "0" " 0" "0 " }
f["not_int_vec0"] = { "0" "1" "2" "" }
f["not_int_vec1"] = { "0" "#" "2" }
f["not_int_vec2"] = { "0" " 1$" "2" }
f.as<string>("int_vec") = "01 2"
f.as<string>("true_vec") = "0 1 0"
f.as<string>("false_vec") = "0 00 "
f.as<string>("not_int_vec0") = "012"
f.as<string>("not_int_vec1") = "0#2"
f.as<string>("not_int_vec2") = "0 1$2"
f.as<vector<int>>("int_vec") = { 0 1 2 }
f.as<bool>("true_vec") = true
f.as<bool>("false_vec") = false
f.as<vector<int>>("not_int_vec0")...Cannot convert "" to int
f.as<vector<int>>("not_int_vec1")...Cannot convert "#" to int
f.as<vector<int>>("not_int_vec2")...Cannot convert " 1$" to int
(gcc 5.1, clang 3.6, C++11)

Convert std::duration to human readable time

Is there a standard implementation to print std::duration as a human readable duration?
steady_clock::time_point start = steady_clock::now();
doSomeFoo();
steady_clock::time_point end = steady_clock::now();
std::cout << "Operation took "
<< may_be_std::theMagic(start-end) << std::endl;
Which should print something similar to:
"Operation took 10d:15h:12m:14:s"
or something similar.
Agreed there is no standard implementation. Here is how you can write one yourself:
#include <iostream>
#include <iomanip>
#include <chrono>
std::ostream&
display(std::ostream& os, std::chrono::nanoseconds ns)
{
using namespace std;
using namespace std::chrono;
typedef duration<int, ratio<86400>> days;
char fill = os.fill();
os.fill('0');
auto d = duration_cast<days>(ns);
ns -= d;
auto h = duration_cast<hours>(ns);
ns -= h;
auto m = duration_cast<minutes>(ns);
ns -= m;
auto s = duration_cast<seconds>(ns);
os << setw(2) << d.count() << "d:"
<< setw(2) << h.count() << "h:"
<< setw(2) << m.count() << "m:"
<< setw(2) << s.count() << 's';
os.fill(fill);
return os;
};
int
main()
{
std::cout << "Operation took ";
display(std::cout, std::chrono::microseconds(918734000000));
std::cout << '\n';
}
Operation took 10d:15h:12m:14s
Based on Howard's answer, I wrote this to make sure that only the relevant data is printed out, so 120 seconds becomes 2m00s instead of 00d:00h:02m00s, and made sure to strip the leading zero, so its still 2m00s and not 02m00s.
Usage is simple:
std::chrono::seconds seconds{60*60*24 + 61};
std::string pretty_seconds = beautify_duration(seconds);
printf("seconds: %s", pretty_seconds.c_str());
>>seconds: 1d00h01m01s
Code:
std::string beautify_duration(std::chrono::seconds input_seconds)
{
using namespace std::chrono;
typedef duration<int, std::ratio<86400>> days;
auto d = duration_cast<days>(input_seconds);
input_seconds -= d;
auto h = duration_cast<hours>(input_seconds);
input_seconds -= h;
auto m = duration_cast<minutes>(input_seconds);
input_seconds -= m;
auto s = duration_cast<seconds>(input_seconds);
auto dc = d.count();
auto hc = h.count();
auto mc = m.count();
auto sc = s.count();
std::stringstream ss;
ss.fill('0');
if (dc) {
ss << d.count() << "d";
}
if (dc || hc) {
if (dc) { ss << std::setw(2); } //pad if second set of numbers
ss << h.count() << "h";
}
if (dc || hc || mc) {
if (dc || hc) { ss << std::setw(2); }
ss << m.count() << "m";
}
if (dc || hc || mc || sc) {
if (dc || hc || mc) { ss << std::setw(2); }
ss << s.count() << 's';
}
return ss.str();
}
Here is a version that allows you to inline a duration with operator<<
It only prints what is necessary and allows setting the precision you want:
#include <chrono>
#include <iomanip>
#include <optional>
#include <ostream>
std::ostream& operator<<(std::ostream& os, std::chrono::nanoseconds ns)
{
using namespace std::chrono;
using days = duration<int, std::ratio<86400>>;
auto d = duration_cast<days>(ns);
ns -= d;
auto h = duration_cast<hours>(ns);
ns -= h;
auto m = duration_cast<minutes>(ns);
ns -= m;
auto s = duration_cast<seconds>(ns);
ns -= s;
std::optional<int> fs_count;
switch (os.precision()) {
case 9: fs_count = ns.count();
break;
case 6: fs_count = duration_cast<microseconds>(ns).count();
break;
case 3: fs_count = duration_cast<milliseconds>(ns).count();
break;
}
char fill = os.fill('0');
if (d.count())
os << d.count() << "d ";
if (d.count() || h.count())
os << std::setw(2) << h.count() << ":";
if (d.count() || h.count() || m.count())
os << std::setw(d.count() || h.count() ? 2 : 1) << m.count() << ":";
os << std::setw(d.count() || h.count() || m.count() ? 2 : 1) << s.count();
if (fs_count.has_value())
os << "." << std::setw(os.precision()) << fs_count.value();
if (!d.count() && !h.count() && !m.count())
os << "s";
os.fill(fill);
return os;
}
Here are some usage examples:
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono_literals;
int main()
{
cout << 918734032564785ns << "\n";
cout << setprecision(3) << 918734032564785ns << "\n";
cout << setprecision(9) << 918734032564785ns << "\n";
cout << setprecision(0) << 918734032564785ns << "\n";
cout << setprecision(3) << 432034ms << "\n";
cout << 14h + 32min + 37s + 645ms << "\n";
cout << 86472s << "\n";
cout << 4324ms << "\n";
return 0;
}
Output:
10d 15:12:14.032564
10d 15:12:14.032
10d 15:12:14.032564785
10d 15:12:14
7:12.034
14:32:37.645
1d 00:01:12.000
4.324s
No, there is no standard implementation. You can get a human readable version of a std::chrono::time_point via std::put_time but not a std::chrono::duration.
Here is a templated version which works with all timeunits from chrono.
Credit goes to the responders before me.
template<typename T>
inline std::string format(T timeunit) {
nanoseconds ns = duration_cast<nanoseconds>(timeunit);
std::ostringstream os;
bool foundNonZero = false;
os.fill('0');
typedef duration<int, std::ratio<86400*365>> years;
const auto y = duration_cast<years>(ns);
if (y.count()) {
foundNonZero = true;
os << y.count() << "y:";
ns -= y;
}
typedef duration<int, std::ratio<86400>> days;
const auto d = duration_cast<days>(ns);
if (d.count()) {
foundNonZero = true;
os << d.count() << "d:";
ns -= d;
}
const auto h = duration_cast<hours>(ns);
if (h.count() || foundNonZero) {
foundNonZero = true;
os << h.count() << "h:";
ns -= h;
}
const auto m = duration_cast<minutes>(ns);
if (m.count() || foundNonZero) {
foundNonZero = true;
os << m.count() << "m:";
ns -= m;
}
const auto s = duration_cast<seconds>(ns);
if (s.count() || foundNonZero) {
foundNonZero = true;
os << s.count() << "s:";
ns -= s;
}
const auto ms = duration_cast<milliseconds>(ns);
if (ms.count() || foundNonZero) {
if (foundNonZero) {
os << std::setw(3);
}
os << ms.count() << ".";
ns -= ms;
foundNonZero = true;
}
const auto us = duration_cast<microseconds>(ns);
if (us.count() || foundNonZero) {
if (foundNonZero) {
os << std::setw(3);
}
os << us.count() << ".";
ns -= us;
}
os << std::setw(3) << ns.count() << "ns" ;
return os.str();
}
Output:
59y:325d:20h:33m:19s:008.800.999ns
20d:13h:53m:19s:008.800.999ns
33m:19s:008.800.999ns
1s:000.000.999ns
1.000.099ns
10.000ns
100ns