I am trying to parse a string of the form:
f 1 2 3 4 f 1 2 3 f 1 2 3 4 5
Using boost spirit, I have:
using boost::spirit::qi::uint_;
using boost::spirit::qi::double_;
using boost::spirit::qi::_1;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
using boost::phoenix::ref;
using boost::phoenix::push_back;
std::vector<unsigned int> v;
r = phrase_parse(first_, last_,
// Begin grammar
(
'f' >> uint_[push_back(ref(v), _1)] >>*(uint_[push_back(ref(v), _1)])
)
,
// End grammar
space);// Begin grammar
However, I am getting a bunch of compile errors:
Error C2064 term does not evaluate to a function taking 2 arguments
Error C2668 'boost::phoenix::ref': ambiguous call to overloaded function
Error C2780 'bool boost::spirit::qi::phrase_parse(Iterator &,Iterator,Expr &,const Skipper &,boost::spirit::qi::skip_flag)': expects 5 arguments - 3 provided
Not really sure whats wrong because the form just follows the example program given in the boost spirit documentation.
Also, note that I am using boost 1_55 with MSVC 2015 ( can't really change these)
For a fixed number of integers after the 'f' the following compiles and works:
unsigned int v1 = 0, v2 = 0, v3 = 0;
r = phrase_parse(first_, last_,
// Begin grammar
(
'f' >> uint_[ref(v1) = _1]
>> uint_[ref(v2) = _1] >> uint_[ref(v3) = _1]
),
// End grammar
space);
Your code isn't self contained, but we can guess that ref
is ambgious, since std::ref
will be picked due to std::vector
being declared in namespace std
: ADL.
(That problem doesn't occur with the separate v1
, v2
, v3
since primitive types do not have an associated namespace).
Therefore being less liberal (more hygienic) with the using declarations this compiles fine for me on Boost 1.55.0:
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/version.hpp>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
std::string values(std::vector<unsigned> const& v) {
std::string r = "[";
for (auto& el : v)
r += (r.length() > 1 ? "," : "") + std::to_string(el);
return r + "]";
}
int main() {
std::cout << "Boost: " << BOOST_VERSION << " Spirit: " << std::hex << SPIRIT_VERSION << std::dec
<< std::endl;
for (std::string const input :
{"f ", "f1", "f", "f-3", "f1f2", "f 1 2 3 4 1 2 3 1 2 3 4 5", "f 1 2 3 4 f 1 2 3 f 1 2 3 4 5"}) {
std::vector<unsigned int> v;
std::string::const_iterator first_ = input.begin(), last_ = input.end();
bool r = qi::phrase_parse( //
first_, last_,
('f' >> qi::uint_[px::push_back(px::ref(v), qi::_1)] >>
*(qi::uint_[px::push_back(px::ref(v), qi::_1)])),
qi::space);
std::cout << (r ? "OK" : "FAIL") << "\t" //
<< input << " -> " << values(v) //
<< " remain '" << std::string(first_, last_) << "'" //
<< std::endl;
}
}
Tested locally with Boost 1.55/Spirit v2.53:
However, there's no need for any of the complexity.
any sequence p >> *p
is by definition equivalent to +p
bool r = qi::phrase_parse( //
first_, last_, //
'f' >> +qi::uint_[px::push_back(px::ref(v), qi::_1)], //
qi::space);
push-back (back-insertion) is already the default action for container attributes, so why have semantic actions?
bool r = qi::phrase_parse(first_, last_, 'f' >> +qi::uint_, qi::space, v);
This has the exact same effect without the compilation overhead and code complexity. You'd never run into the ADL snag in the first place.
**Live On Coliru**¹
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/version.hpp>
namespace qi = boost::spirit::qi;
std::string values(std::vector<unsigned> const& v) {
std::string r = "[";
for (auto& el : v)
r += (r.length() > 1 ? "," : "") + std::to_string(el);
return r + "]";
}
int main() {
std::cout << "Boost: " << BOOST_VERSION << " Spirit: " << std::hex << SPIRIT_VERSION << std::dec
<< std::endl;
for (std::string const input :
{"f ", "f1", "f", "f-3", "f1f2", "f 1 2 3 4 1 2 3 1 2 3 4 5", "f 1 2 3 4 f 1 2 3 f 1 2 3 4 5"}) {
std::vector<unsigned int> v;
auto f = input.begin(), l = input.end();
bool r = qi::phrase_parse(f, l, 'f' >> +qi::uint_, qi::space, v);
std::cout << (r ? "OK" : "FAIL") << "\t" //
<< input << " -> " << values(v) //
<< " remain '" << std::string(f, l) << "'" //
<< std::endl;
}
}
Printing the exact same output.
¹ note how the simplification also removed the dependance on now-deprecated Phoenix headers