I want to parse using the boost spirit the following string:
"{"DeliverNr":7424,"fruits":[["apple","banana","orange", "raspberry"]]}"
but I want to put into vector only three first fruits.
I have the following output:
it: { , dist: 0
Can anybody tell me what am I doing wrong ?
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <vector>
struct SomeStructF
{
std::string str1, str2, str3;
};
using namespace boost::spirit;
qi::rule<std::string::iterator> startRule;
qi::rule<std::string::iterator> endRule;
qi::rule<std::string::iterator, std::string()> stringRule;
qi::rule<std::string::iterator, SomeStructF()> valueRule;
qi::rule<std::string::iterator, std::vector<SomeStructF>()> valuesRule;
char const QUOTATION_MARK{ '"' };
char const OPEN_SQUARE_BRACKET{ '[' };
startRule =
+(qi::char_ - qi::char_(OPEN_SQUARE_BRACKET))
>> qi::char_(OPEN_SQUARE_BRACKET);
endRule = +qi::char_;
stringRule =
QUOTATION_MARK
>> *(qi::char_ - QUOTATION_MARK)
>> QUOTATION_MARK;
valueRule =
OPEN_SQUARE_BRACKET
>> stringRule
>> stringRule
>> stringRule;
valuesRule %=
startRule
>> valueRule
>> endRule;
std::vector<SomeStructF> res;
auto it = data.begin();
if (qi::parse(it, data.end(), valuesRule, res))
{
std::cout << "PARSE succeded" << std::endl;
}
std::cout << "it: " << *it << " , dist: " << std::distance(data.begin(), it) << std::endl;
std::cout << "res size " << res.size() << std::endl;
BOOST_FUSION_ADAPT_STRUCT(
parsers::SomeStructF,
(std::string, str1)
(std::string, str2)
(std::string, str3)
)
It's not particularly clear to me what you are asking. It seems to me it's easy enough to take into acocunt the commas, as you noted:
Live On Coliru
#define BOOST_SPIRIT_DEBUG
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace qi = boost::spirit::qi;
struct SomeStructF {
std::string str1, str2, str3;
};
BOOST_FUSION_ADAPT_STRUCT(
SomeStructF,
(std::string, str1)
(std::string, str2)
(std::string, str3)
)
int main()
{
qi::rule<std::string::iterator> startRule;
qi::rule<std::string::iterator> endRule;
qi::rule<std::string::iterator, std::string()> stringRule;
qi::rule<std::string::iterator, SomeStructF()> valueRule;
qi::rule<std::string::iterator, SomeStructF()> valuesRule;
char const QUOTATION_MARK{ '"' };
char const OPEN_SQUARE_BRACKET{ '[' };
startRule =
+(qi::char_ - qi::char_(OPEN_SQUARE_BRACKET))
>> qi::char_(OPEN_SQUARE_BRACKET);
endRule = +qi::char_;
stringRule =
QUOTATION_MARK
>> *(qi::char_ - QUOTATION_MARK)
>> QUOTATION_MARK;
valueRule =
OPEN_SQUARE_BRACKET
>> stringRule >> ','
>> stringRule >> ','
>> stringRule;
valuesRule %=
startRule
>> valueRule
>> endRule;
BOOST_SPIRIT_DEBUG_NODES((startRule)(endRule)(stringRule)(valueRule)(valuesRule));
std::string data = R"({"DeliverNr":7424,"fruits":
[["apple","banana","orange","raspberry"]]})";
std::vector<SomeStructF> res;
auto it = data.begin();
if (qi::parse(it, data.end(), valuesRule, res))
{
std::cout << "PARSE succeded" << std::endl;
std::cout << "res size " << res.size() << std::endl;
for (auto& el : res) {
std::cout << "el: {" << std::quoted(el.str1) << ","
<< std::quoted(el.str2) << ","
<< std::quoted(el.str3) << "}\n";
}
}
else
{
std::cout << "Parse did not succeed\n";
}
if (it != data.end())
std::cout << "Remaining unparsed: '" << std::string(it, data.end()) << "'\n";
}
Prints
PARSE succeded
res size 1
el: {"apple","banana","orange"}
Your grammar (nor your question) shows how you'd like to detect multiple SomeStructF so I can't really know what you want. Perhaps you want to simplify:
Live On Coliru
using It = std::string::const_iterator;
qi::rule<It, std::string()> quoted_ = '"' >> *~qi::char_('"') >> '"';
qi::rule<It, SomeStructF()/*, qi::space_type*/> value_ = quoted_ >> ',' >> quoted_ >> ',' >> quoted_;
using boost::spirit::repository::qi::seek;
BOOST_SPIRIT_DEBUG_NODES((quoted_)(value_));
std::string const data = R"({"DeliverNr":7424,"fruits":[["apple","banana","orange","raspberry"]]}
{"DeliverNr":7435,"botanics":[["pine","maple","oak","pernambucco"]]})";
std::vector<SomeStructF> res;
It it = data.begin();
if (qi::phrase_parse(it, data.end(), *seek["[[" >> value_], qi::space, res)) {
for (auto& el : res) {
std::cout << "el: {" << std::quoted(el.str1) << ","
<< std::quoted(el.str2) << ","
<< std::quoted(el.str3) << "}\n";
}
} else {
std::cout << "Parse did not succeed\n";
}
Which prints
el: {"apple","banana","orange"}
el: {"pine","maple","oak"}
Remaining unparsed: ',"pernambucco"]]}'
Related
I need to parse a key value pair, where key itself is a fixed string lke 'cmd' in the example. Unfortunately qi::lit has no synthesized attribute and qi::char_ parses no fixed string.
Following code does not compile. I would need that result.name == cmd after execution.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
#include <string>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
struct CommandRuleType
{
std::string name;
int arg;
};
BOOST_FUSION_ADAPT_STRUCT(CommandRuleType, name, arg)
int main() {
qi::rule<std::string::const_iterator, CommandRuleType(), qi::space_type> rule = qi::lit("cmd") >> "=" >> qi::int_;
for (std::string const s : {"cmd = 1" }) {
std::cout << std::quoted(s) << " -> ";
CommandRuleType result;
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << result.name << "=" << result.arg << "\n";
} else {
std::cout << "parse failed\n";
}
}
}
qi::lit does not expose an attribute. qi::string does:
rule = qi::string("cmd") >> "=" >> qi::int_;
Live On Coliru
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <string>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
struct CommandRuleType {
std::string name;
int arg;
};
BOOST_FUSION_ADAPT_STRUCT(CommandRuleType, name, arg)
int main() {
qi::rule<std::string::const_iterator, CommandRuleType(), qi::space_type>
rule = qi::string("cmd") >> "=" >> qi::int_;
for (std::string const s : { "cmd = 1" }) {
std::cout << std::quoted(s) << " -> ";
CommandRuleType result;
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << result.name << "=" << result.arg << "\n";
} else {
std::cout << "parse failed\n";
}
}
}
Prints
"cmd = 1" -> result: cmd=1
everyone!
I'm new to C++, alas I make silly mistakes.
This is a snippet of a .txt-file's content:
<tag attr1="value1" attr2="value2" ... >
What I'm trying to accomplish is parsing through the .txt-file, generating the following output:
Tag: tag
name: attr1
value: value1
name: attr2
value: value2
What I've done so far didn't work (my problem is the delimiters):
#include<iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
struct tagline{
string tag;
string attributeN;
string attributeV;
};
int main(){
vector<tagline> information;
string line;
tagline t;
ifstream readFile("file.txt");
while(getline(readFile,line)){
stringstream in(line);
getline(in,t.tag);
getline(in,t.attributeN,'=');
getline(in,t.attributeV,'"');
information.push_back(t);
}
vector<tagline>::iterator it = information.begin();
for(; it != information.end(); it++){
cout << "Tag: " << (*it).tag << " \n"
<< "name: " << (*it).attributeN << " \n"
<< "value: " << (*it).attributeV << " \n";
}
return 0;
}
All I get is a plain display of the snippet as it's formatted in the .txt-file:
<tag attr1="value1" attr2="value2" ... >
I would be happy if someone could help. Thank you!
This would be better handled using an HTML/XML parser (depending on what your file actually contains).
That being said, you are not parsing the lines correctly.
Your first call to getline(in,t.tag); is not specifying a delimiter, so it reads the entire line, not just the first word. You would have to use getline(in, t.tag, ' '); instead.
Also, your tags can have multiple attributes, but you are only reading and storing the first attribute, ignoring the rest. You need a loop to read all of them, and a std::vector to store them all into.
Try something more like this instead:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
struct tagattribute {
string name;
string value;
};
struct tagline {
string tag;
vector<tagattribute> attributes;
};
int main() {
vector<tagline> information;
string line;
ifstream readFile("file.txt");
while (getline(readFile, line)) {
istringstream in(line);
tagline t;
tagattribute attr;
in >> ws;
char ch = in.get();
if (ch != '<')
continue;
if (!(in >> t.tag))
continue;
do
{
in >> ws;
ch = in.peek();
if (ch == '>')
break;
if (getline(in, attr.name, '=') &&
in.ignore() &&
getline(in, attr.value, '"'))
{
t.attributes.push_back(attr);
}
else
break;
}
while (true);
information.push_back(t);
}
vector<tagline>::iterator it = information.begin();
for(; it != information.end(); ++it) {
cout << "Tag: " << it->tag << "\n";
vector<tagattribute>::iterator it2 = it->attributes.begin();
for(; it2 != it->attributes.end(); ++it2) {
cout << "name: " << it2->name << "\n"
<< "value: " << it2->value << "\n";
}
cout << "\n";
}
return 0;
}
Live demo
Alternatively, consider writing some custom operator>> to help with the parsing, eg:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
struct tagattribute {
string name;
string value;
};
istream& operator>>(istream &in, tagattribute &attr)
{
getline(in, attr.name, '=');
in.ignore();
getline(in, attr.value, '"');
return in;
}
struct tagline {
string tag;
vector<tagattribute> attributes;
};
istream& operator>>(istream &in, tagline &t)
{
tagattribute attr;
in >> ws;
char ch = in.get();
if (ch != '<')
{
in.setstate(ios_base::failbit);
return in;
}
if (!(in >> t.tag))
return in;
do
{
in >> ws;
ch = in.peek();
if (ch == '>')
{
in.ignore();
break;
}
if (!(in >> attr))
break;
t.attributes.push_back(attr);
}
while (true);
return in;
}
int main() {
vector<tagline> information;
string line;
ifstream readFile("file.txt");
while (getline(readFile, line)) {
istringstream in(line);
tagline t;
if (in >> t)
information.push_back(t);
}
vector<tagline>::iterator it = information.begin();
for(; it != information.end(); ++it) {
cout << "Tag: " << it->tag << "\n";
vector<tagattribute>::iterator it2 = it->attributes.begin();
for(; it2 != it->attributes.end(); ++it2) {
cout << "name: " << it2->name << "\n"
<< "value: " << it2->value << "\n";
}
cout << "\n";
}
return 0;
}
Live demo
Well, I would try to do something like this using this wonderful answer:
struct xml_skipper : std::ctype<char> {
xml_skipper() : ctype(make_table()) { }
private:
static mask* make_table() {
const mask* classic = classic_table();
static std::vector<mask> v(classic, classic + table_size);
v[','] |= space;
v['"'] |= space;
v['='] |= space;
v['<'] |= space;
v['>'] |= space;
return &v[0];
}
};
Then, what you can do is just keep reading:
ifstream readFile("file.txt");
while(getline(readFile,line)){
istringstream in(line);
in.imbue(std::locale(in.getloc(), new xml_skipper));
in >> t.tag >> t.attributeN >> t.attributeV;
information.push_back(t);
}
//...
Do note that this will break if values or attribute names have whitespaces.
If you want something more serious, you will need to write lexer, syntax tree builder and semantics tree builder.
Full code
#include<iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
using namespace std;
struct tagline{
string tag;
string attributeN;
string attributeV;
};
struct xml_skipper : std::ctype<char> {
xml_skipper() : ctype(make_table()) { }
private:
static mask* make_table() {
const mask* classic = classic_table();
static std::vector<mask> v(classic, classic + table_size);
v[','] |= space;
v['"'] |= space;
v['='] |= space;
v['<'] |= space;
v['>'] |= space;
return &v[0];
}
};
int main(){
vector<tagline> information;
string line;
tagline t;
std::istringstream readFile{"<tag attr1=\"value1\" attr2=\"value2\" ... >"};
while(getline(readFile,line)){
istringstream in(line);
in.imbue(std::locale(in.getloc(), new xml_skipper));
in >> t.tag >> t.attributeN >> t.attributeV;
information.push_back(t);
}
vector<tagline>::iterator it = information.begin();
for(; it != information.end(); it++){
cout << "Tag: " << (*it).tag << " \n"
<< "name: " << (*it).attributeN << " \n"
<< "value: " << (*it).attributeV << " \n";
}
}
Live on Wandbox.
If your input may vary within the boundaries of the xml specification, an XML-parser might be a better approach than parsing the string "manually".
Just to show how this could look like, see the following code. It is based on tinyxml2, which just requires to include a single .cpp / .h-file in your project. You could - of course - also use any other xml libraries; this is just for demonstration purpose:
#include <iostream>
#include "tinyxml2.h"
using namespace tinyxml2;
int main()
{
const char* test = "<tag attr1='value1' attr2 = \"value2\"/>";
XMLDocument doc;
doc.Parse(test);
XMLElement *root = doc.RootElement();
if (root) {
cout << "Tag: " << root->Name() << endl;
const XMLAttribute *attrib = root->FirstAttribute();
while (attrib) {
cout << "name: " << attrib->Name() << endl;
cout << "value : " << attrib->Value() << endl;
attrib = attrib->Next();
}
}
}
I need to parse a string into a boolean value; in particular
if the string is ack I want the parser sets a boolean variable with true;
if the string is noack I want the parser sets a boolean variable with false;
Note that I do not want to use the return value of the function qi::parse; I want the qi::parse function sets a boolean variable given as argument. Something like the following (not compiling) code:
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
using namespace boost::spirit;
int main() {
bool b;
std::string s("ack");
auto it = s.begin();
if (qi::parse(it, s.end(), < need a rule here... >, b))
std::cout << "Value: " << std::boolalpha << b << std::endl;
return 0;
}
Thanks a lot in advance.
The simplest approach that comes to mind is "noack" >> qi::attr(false) and its complement. Add an eoi to ensure full input is parsed:
Live On Coliru
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
int main() {
for (std::string const s : {"ack","noack","oops","ack 2",""}) {
bool b;
if (qi::parse(s.begin(), s.end(), ("ack" >> qi::attr(true) | "noack" >> qi::attr(false)) >> qi::eoi, b))
std::cout << "'" << s << "' -> " << std::boolalpha << b << std::endl;
else
std::cout << "'" << s << "' -> FAIL\n";
}
}
Prints
'ack' -> true
'noack' -> false
'oops' -> FAIL
'ack 2' -> FAIL
'' -> FAIL
BONUS:
Use symbol for more elegance:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
struct acknoack_type : qi::symbols<char, bool> {
acknoack_type() { this->add("ack", true)("noack", false); }
} static const acknoack;
int main() {
for (std::string const s : {"ack","noack","oops","ack 2",""}) {
bool b;
if (qi::parse(s.begin(), s.end(), acknoack >> qi::eoi, b))
std::cout << "'" << s << "' -> " << std::boolalpha << b << std::endl;
else
std::cout << "'" << s << "' -> FAIL\n";
}
}
I can parse one float and print it. (test1, test2)
Somehow I am unable to build a rule that parses three floats.
My final goal is to parse three floats and save them to a glm::vec3.
My rule seems to be incorrect:
qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_]
I am not sure if i am using BOOST_FUSION_ADAPT_STRUCT correctly
Here is a source to show what i came up with so far:
#include <iostream>
#include <string>
#include <glm\glm.hpp>
#include <boost\spirit\include\qi.hpp>
#include <boost\fusion\include\adapt_struct.hpp>
namespace qi = boost::spirit::qi;
void test1()
{
std::string test = "1.2";
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), qi::float_, qi::space) && (it == test.end()))
std::cout << "test 1 ok" << std::endl;
else
std::cout << "test 1 not ok" << std::endl;
}
void test2()
{
std::string test = "1.2";
auto it = test.begin();
float f;
if (qi::phrase_parse(it, test.end(), qi::float_, qi::space, f) && (it == test.end()))
std::cout << "test 2 ok " << f << std::endl;
else
std::cout << "test 2 not ok" << std::endl;
}
void test3()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space) && (it == test.end()))
std::cout << "test 3 ok " << std::endl;
else
std::cout << "test 3 not ok" << std::endl;
}
BOOST_FUSION_ADAPT_STRUCT(
glm::vec3,
(float, x)
(float, y)
(float, z)
)
void test4()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
glm::vec3 result;
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
{
std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
}
else
std::cout << "test 4 not ok" << std::endl;
}
int main(int argc, char ** argv)
{
test1();
test2();
test3();
test4();
}
The test4 function contains everything I try to get done.
EDIT:
As suggested two things need to be changed:
void test4()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
glm::vec3 result;
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
{
std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
}
else
std::cout << "test 4 not ok" << std::endl;
}
I agree with Pete Becker. Simpler is usually better.
Now, since you are going to end up using spirit, let's look at what can be simpler:
use c++11 adapt:
BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
you can do without the adapt (simpler in the demo)
you can (should) drop the skipper from the rule declaration if you're doing a toplevel lexeme[] directive anyways¹. See stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965
better yet, do without the rule for readability here. In reality, you would have a larger grammar with multiple rules. Be sure not to instantiate the grammar each parse (for efficiency).
Demo testbed Live On Coliru
//#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <glm/glm.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
glm::vec3 simpler(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (parse(it, test.end(),
float_ >> ' ' >> float_ >> ' ' >> float_ >> eoi,
result.x, result.y, result.z))
{
return result;
}
throw std::runtime_error("invalid input");
}
glm::vec3 use_skipper(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (phrase_parse(it, test.end(),
float_ >> float_ >> float_ >> eoi,
space, result.x, result.y, result.z))
{
return result;
}
throw std::runtime_error("invalid input");
}
BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
glm::vec3 with_adapt(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (phrase_parse(it, test.end(), float_ >> float_ >> float_ >>eoi, space, result))
{
return result;
}
throw std::runtime_error("invalid input");
}
int main() {
struct { glm::vec3 (&f)(std::string const&); std::string name; } tests[] = {
{simpler, "simpler"},
{use_skipper, "use_skipper"},
{with_adapt, "with_adapt"}
};
for (auto t : tests) try {
glm::vec3 v = t.f("1.2 2.2 3.3");
std::cout << t.name << " ok (" << v.x << ", " << v.y << ", " << v.z << ")\n";
} catch(std::exception const& e) {
std::cout << t.name << " fail (" << e.what() << ")\n";
}
}
You can be slightly more lightweight Using x3. Not really less work, but certainly less heavy on the compiler.
¹ subtle difference w.r.t. pre/post skipping, but that difference would be gone if there's a containing grammar with skipper
The sequence parser operator is >>, use that instead of << in your rules
VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
You've specified a whitespace skipper in your rule, so it can be further simplified by removing the lexeme directive and letting the skipper skip spaces automatically (unless you want to ensure there is a single space character in between the inputs).
VEC3 = qi::float_ >> qi::float_ >> qi::float_;
Also, if you want your rule to return a value, you need to add that signature to the rule type
qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
This is unrelated to the compilation error you're seeing, but use forward slashes in your #include directives, that works on all platforms.
#include <boost/spirit/include/qi.hpp>
Seems like an awful lot of work for something that's straightforward.
#include <iostream>
#include <string>
#include <sstream>
int main() {
std::string test = "1.2 2.2 3.3";
float f1, f2, f3;
std::istringstream in(test);
in >> f1 >> f2 >> f3;
return 0;
}
I have the following code:
#include <boost/any.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
template <typename Iterator>
struct parser : boost::spirit::qi::grammar<Iterator, boost::any(), boost::spirit::qi::ascii::space_type>
{
parser() : parser::base_type(start)
{
start %= boost::spirit::qi::int_ | boost::spirit::qi::lexeme['"' >> +(boost::spirit::qi::char_ - '"') >> '"']; // example: 0 or "str"
}
boost::spirit::qi::rule<Iterator, boost::any(), boost::spirit::qi::ascii::space_type> start;
};
int main()
{
const std::string input_data("\"str\"");
boost::any var = 0;
auto itr = input_data.begin();
auto end = input_data.end();
parser<decltype(itr)> g;
bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, var);
if (res && itr == end)
{
std::cout << "Parsing succeeded \n";
try
{
std::cout << boost::any_cast<std::string>(var) << '\n';
}
catch (const boost::bad_any_cast& ex)
{
std::cerr << ex.what() << '\n';
}
}
else
{
std::cout << "Parsing failed \n";
}
}
It compiles fine until I add
boost::spirit::qi::lexeme['"' >> +(boost::spirit::qi::char_ - '"') >> '"']
I can't post the errors here and on pastie.org because of the characters amount limitations.
What am I doing wrong? How can I fix it?
Again, why would you want to complicate matters and make it slow by using boost::any?
Anyways, +char_ exposes std::vector<char> and apparently the attribute compatibility rules don't want to decide what to transform this to.
Make it explicit with as_string: Live On Coliru
#include <boost/any.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
template <typename Iterator>
struct parser : boost::spirit::qi::grammar<Iterator, boost::any(), boost::spirit::qi::ascii::space_type>
{
parser() : parser::base_type(start)
{
using namespace boost::spirit::qi;
start %= int_ | as_string [ lexeme['"' >> +(char_ - '"') >> '"'] ]; // example: 0 or "str"
}
boost::spirit::qi::rule<Iterator, boost::any(), boost::spirit::qi::ascii::space_type> start;
};
int main()
{
// const std::string input_data("\"str\"");
const std::string input_data("123");
for (std::string const& input_data : { "\"str\"", "123" })
{
boost::any var = boost::none;
auto itr = input_data.begin();
auto end = input_data.end();
parser<decltype(itr)> g;
bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, var);
if (res && itr == end)
{
std::cout << "Parsing succeeded \n";
try { std::cout << boost::any_cast<int> (var) << '\n'; } catch (const boost::bad_any_cast& ex) { std::cerr << ex.what() << '\n'; }
try { std::cout << boost::any_cast<std::string>(var) << '\n'; } catch (const boost::bad_any_cast& ex) { std::cerr << ex.what() << '\n'; }
}
else
{
std::cout << "Parsing failed \n";
}
}
}