Reputation: 449
see this snippet of code:
using qi::int_;
using qi::phrase_parse;
using qi::_1;
using ascii::space;
std::string s = "50 43.3 77.44";
first = s.begin();
last = s.end();
boost::tuple<boost::optional<double>, boost::optional<int>> to;
bool r = phrase_parse(first, last,
(
double_ || int_
),
space, to); // r == true
auto a = to.get<0>(); // this works catches the 50 from the input
auto aa = *a;
auto b = to.get<1>(); // this does not work even though 43 is parsed correctly
auto bb = *b; // EXCEPTION
I have tried placing this strange behaviour the [email protected] but no one answers
The code is very small. I would truly appreciate if someone tries to compile and runs it!
Regards, Juan
Upvotes: 0
Views: 78
Reputation: 393114
qi::double_ | qi::int_
parses alternatives, not a sequence.
In other words, I'd expect you to use qi::double_ | qi::int_
to parse variant<double, int>
(or compatible).
In contrast, use qi::double_ >> qi::int_
to parse tuple<double, int>
(or compatible).
Now, double_ || int_
is a weird one: it's an optional/alternative sequence: https://www.boost.org/doc/libs/1_74_0/libs/spirit/doc/html/spirit/qi/reference/operator/sequential_or.html. So... what do you expect to happen?
It looks like you're parsing space-separated numbers. There's 3 of them. But you parse into a tuple of two. With an alternative sequence. Of which both branches conflict (any integer will parse as a double, and the integer part of a double can parse as an int, as you very well know, because you claim "even though 43 is parsed correctly").
I'm at a loss what you're trying to achieve, as most people would never say that "43 is parsed correctly" since there is no number 43 in the input (that's only part of 43.3).
Because it's very hard to guess what you were actually trying to parse here, let's avoid choosing and just demonstrate all approaches starting from your example:
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/lexical_cast.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
std::string const sample = "50 43.3 77.44";
template <typename T>
static std::string debug_string(T const& v, std::string const& sep = "") {
return boost::lexical_cast<std::string>(v) + sep;
}
template <typename... T>
static auto debug_string(std::tuple<T...> const& v, std::string const& sep = "") {
std::ostringstream oss;
oss << "( ";
auto out = [&oss](auto const&... elements) {
(oss << ... << debug_string(elements, " ")) << ")";
};
std::apply(out, v);
return oss.str() + sep;
}
template <typename Parser, typename... Attrs>
void demo_parse(Parser const& p, Attrs... attrs) {
auto first = sample.begin();
auto last = sample.end();
if (qi::phrase_parse(first, last, p, qi::space, attrs...)) {
((std::cout << "Parsed: ") << ... << debug_string(attrs, " ")) << "\n";
} else {
std::cout << "Parse failed\n";
}
if (first != last) {
std::cout << "Remaining input: " << std::quoted(std::string(first, last)) << "\n";
}
std::cout << "------------------------\n";
}
int main() {
std::cout << sample << "\n========================\n";
//// as a sequence
std::cout << "As a sequence" << "\n========================\n";
// note that int vs double input can be confusing:
auto parse_sequence = [](auto p) {
double d = 0; int i = 0;
demo_parse(p, d, i);
demo_parse(p, i, d);
demo_parse(p, std::tuple<double, int>{});
demo_parse(p, std::tuple<int, double>{});
demo_parse(p, std::tuple<boost::optional<double>, boost::optional<int> >{});
demo_parse(p, std::tuple<boost::optional<int>, boost::optional<double> >{});
};
parse_sequence(qi::int_ >> qi::double_);
parse_sequence(qi::double_ >> qi::int_);
// as an alternative
std::cout << "As alternatives" << "\n========================\n";
// - keeping in mind the confusion between double_ and int_, using
// strict_real_policies
qi::real_parser<double, qi::strict_real_policies<double> > sdouble_;
demo_parse(sdouble_ | qi::int_, boost::variant<int, double>{});
demo_parse(sdouble_ | qi::int_, boost::variant<double, int>{});
demo_parse(sdouble_ | qi::int_, boost::optional<boost::variant<int, double>>{});
// as alternative sequences
std::cout << "As alternative sequences" << "\n========================\n";
// - I don't know why this would be useful, and keep in mind the
// strict_real_policies again
demo_parse(sdouble_ || qi::int_, std::tuple<int, double>{});
demo_parse(sdouble_ || qi::int_, std::tuple<double, int>{});
demo_parse(sdouble_ || qi::int_, std::tuple<boost::optional<double>, boost::optional<int> >{});
std::cout << "Alternatives into variant type" << "\n========================\n";
demo_parse(sdouble_ || qi::int_, boost::variant<int, double>{});
demo_parse(sdouble_ || qi::int_, boost::variant<double, int>{});
demo_parse(sdouble_ || qi::int_, boost::optional<boost::variant<double, int>> {});
}
Prints
50 43.3 77.44
========================
As a sequence
========================
Parsed: 50 43
Remaining input: "77.44"
------------------------
Parsed: 50 43.299999999999997
Remaining input: "77.44"
------------------------
Parsed: ( 50 43 )
Remaining input: "77.44"
------------------------
Parsed: ( 50 43.299999999999997 )
Remaining input: "77.44"
------------------------
Parsed: ( 50 43 )
Remaining input: "77.44"
------------------------
Parsed: ( 50 43.3 )
Remaining input: "77.44"
------------------------
Parsed: 50 43
Remaining input: ".3 77.44"
------------------------
Parsed: 50 43
Remaining input: ".3 77.44"
------------------------
Parsed: ( 50 43 )
Remaining input: ".3 77.44"
------------------------
Parsed: ( 50 43 )
Remaining input: ".3 77.44"
------------------------
Parsed: ( 50 43 )
Remaining input: ".3 77.44"
------------------------
Parsed: ( 50 43 )
Remaining input: ".3 77.44"
------------------------
As alternatives
========================
Parsed: 50
Remaining input: "43.3 77.44"
------------------------
Parsed: 50
Remaining input: "43.3 77.44"
------------------------
Parsed: 50
Remaining input: "43.3 77.44"
------------------------
As alternative sequences
========================
Parsed: ( 0 50 )
Remaining input: "43.3 77.44"
------------------------
Parsed: ( 0 50 )
Remaining input: "43.3 77.44"
------------------------
Parsed: ( -- 50 )
Remaining input: "43.3 77.44"
------------------------
Alternatives into variant type
========================
Parsed: 0
Remaining input: "43.3 77.44"
------------------------
Parsed: 0
Remaining input: "43.3 77.44"
------------------------
Parsed: --
Remaining input: "43.3 77.44"
------------------------
That's probably overwhelming, so here's what I think you might have been after: just parsing a sequence of int-or-double:
// #define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
std::string const sample = "50 43.3 77.44";
using It = std::string::const_iterator;
int main() {
std::cout << sample << "\n========================\n";
using Value = boost::variant<double, int>;
using Values = std::vector<Value>;
qi::real_parser<double, qi::strict_real_policies<double> > sdouble_;
qi::rule<It, Value()> double_or_int = sdouble_ | qi::int_;
BOOST_SPIRIT_DEBUG_NODE(double_or_int);
auto first = sample.begin();
auto last = sample.end();
Values values;
if (qi::phrase_parse(first, last, *double_or_int, qi::space, values)) {
std::cout << "Parsed:";
for (auto v : values)
std::cout << " " << v;
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}
if (first != last) {
std::cout << "Remaining input: " << std::quoted(std::string(first, last)) << "\n";
}
}
Prints
50 43.3 77.44
========================
Parsed: 50 43.3 77.44
Perhaps the clue was in the weirdness of the claim that "43 is parsed correctly". If 43 is parsed "correctly" that means that the periods (".") are NOT decimal separators. So perhaps you want to parse "1 2 . 3 4 . 5 6 . 7 8" style input?
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
std::string const sample = "50 43.3 77.44";
using It = std::string::const_iterator;
int main() {
std::cout << sample << "\n========================\n";
auto first = sample.begin();
auto last = sample.end();
using Pair = std::pair<int, int>;
std::vector<Pair> values;
if (qi::phrase_parse(first, last, (qi::int_ >> qi::int_) % '.', qi::space, values)) {
std::cout << "Parsed:";
for (auto [a,b] : values)
std::cout << " (" << a << " " << b << ")";
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}
if (first != last) {
std::cout << "Remaining input: " << std::quoted(std::string(first, last)) << "\n";
}
}
Prints
50 43.3 77.44
========================
Parsed: (50 43) (3 77)
Remaining input: ".44"
Upvotes: 2