I am writing a c++ application with several complex structs and
I want to read a string and fill those structs by data provided in that text.
But for easier understanding and debugging, i've wrote easy program with same problem.
This is my code:
#include <string>
#include <iostream>
#define FUSION_MAX_VECTOR_SIZE 30
#define BOOST_PHOENIX_USE_V2_OVER_V3
#include <boost/spirit/home/phoenix/bind/bind_function.hpp>
#include <boost/phoenix/bind.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
using qi::double_;
using qi::char_;
using qi::lexeme;
using qi::int_;
using qi::lit;
using qi::_1;
using ascii::space;
using phoenix::ref;
using qi::parser;
class Test
{
// Class fields
std::string test_str;
public:
Test(std::string& sample_str)
{
test_str = sample_str;
}
struct fruit
{
std::string name;
std::string color;
};
BOOST_FUSION_ADAPT_STRUCT
(
fruit,
(std::string, name)
(std::string, color)
);
struct person
{
std::string name;
int age;
};
BOOST_FUSION_ADAPT_STRUCT
(
person,
(std::string, name)
(int, age)
);
void received_person(person& p)
{
std::cout << p.name << " with age"<< p.age<< " has been seen!"<<std::endl;
}
void received_fruit(fruit& f)
{
std::cout << f.name<<" is "<<f.color<<std::endl;
}
template <typename Iterator>
struct MyGrammar : boost::spirit::qi::grammar<Iterator, void()>
{
MyGrammar() : MyGrammar::base_type(my_item)
{
my_item = *(fruit[ boost::phoenix::bind(&received_fruit, boost::spirit::_1 )]
|
_person[ boost::phoenix::bind(&received_person, boost::spirit::_1 )]
);
_person = qi::lit('(') >> *(qi::char_ - ',') >> ',' >> qi::int_ >> ')';
_fruit = qi::lit('[') >> *(qi::char_ - ',') >> ',' >> *(qi::char_) >> ']';
}
qi::rule<Iterator, void()> my_item;
qi::rule<Iterator, person()> _person;
qi::rule<Iterator, fruit()> _fruit;
};
void run()
{
typedef std::string::const_iterator iterator;
MyGrammar <std::string::const_iterator> my_grammar;
std::string::const_iterator begin = test_str.begin();
std::string::const_iterator end = test_str.end();
bool result_ = qi::parse(begin, end, my_grammar) && begin == end;
}
};
int main()
{
std::string input("(jane, 23000)(david, 19)(mary, 30)[yello,100][green, 60.6][red, 30.5]");
Test test(input);
test.run();
return 0;
}
g++ compiles this code and generates this error:
expected unqualified-id before 'namespace'
i know this code can be written without using class, but i want to use class in main project. many thanks in advance!
BOOST_FUSION_ADAPT_STRUCT needs to be at global namespace
The macro should be used at global scope, and struct_name should be the fully namespace qualified name of the struct to be adapted. [documentation]
you needed a skipper to allow for whitespace as in your sample input
qi::phrase_parse
to supply a skipperbegin == end
was redundant (because qi::parse
always consumes the whole input on success AFAIR)qi::lexeme
to control when you expect whitespace as part of the parsed contentyou needed the callbacks to be static
_fruit
rule to read until the closing ]
With these taken care of, your code works: See it Live on Coliru
Just in case you didn't know, you don't have to jump through all these fusion/phoenix hoops if you don't want. You'd usually parse into datastructures first, and then process there.
See this demonstrated Live On Coliru too.
E.g., data structures:
namespace ast {
struct fruit {
std::string name;
std::string color;
};
struct person {
std::string name;
int age;
};
typedef boost::variant<fruit, person> record;
typedef std::vector<record> records;
// for our demo output:
static std::ostream& operator<<(std::ostream& os, fruit const& f);
static std::ostream& operator<<(std::ostream& os, person const& p);
}
And the code that handles it (note improved status reporting):
int main()
{
typedef std::string::const_iterator It;
std::string const input("(jane, 23000)(david, 19)(mary, 30)[yello,100][green, 60.6][red, 30.5]");
MyGrammar <It> my_grammar;
It begin(input.begin()), end(input.end());
ast::records data;
if (qi::phrase_parse(begin, end, my_grammar, qi::space, data))
{
std::cout << "Parse success\n";
for (auto const& record : data)
std::cout << record << "\n";
}
else
std::cout << "Parse failed\n";
if (begin != end)
std::cout << "Remaining unparsed: '" << std::string(begin, end) << "'\n";
}
Now, the clincher is, the grammar becamse simpler too (no more phoenix; I assumed that names/colors should not contain ,
,]
or )
):
_text = qi::lexeme [ *~qi::char_(",)]") ];
_person = qi::lit('(') >> _text >> ',' >> qi::int_ >> ')';
_fruit = qi::lit('[') >> _text >> ',' >> _text >> ']';
_start = *(_fruit | _person);
Here's the full code listing:
#include <string>
#include <iostream>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace ast {
struct fruit {
std::string name;
std::string color;
friend std::ostream& operator<<(std::ostream& os, fruit const& f) { return os << f.name << " is " << f.color; }
};
struct person {
std::string name;
int age;
friend std::ostream& operator<<(std::ostream& os, person const& p) { return os << p.name << " with age" << p.age << " has been seen!"; }
};
typedef boost::variant<fruit, person> record;
typedef std::vector<record> records;
}
BOOST_FUSION_ADAPT_STRUCT(ast::fruit,
(std::string, name)
(std::string, color))
BOOST_FUSION_ADAPT_STRUCT(ast::person,
(std::string, name)
(int, age))
template <typename Iterator, typename Skipper = qi::space_type>
struct MyGrammar : qi::grammar<Iterator, ast::records(), Skipper>
{
MyGrammar() : MyGrammar::base_type(_start)
{
_text = qi::lexeme [ *~qi::char_(",)]") ];
_person = qi::lit('(') >> _text >> ',' >> qi::int_ >> ')';
_fruit = qi::lit('[') >> _text >> ',' >> _text >> ']';
_start = *(_fruit | _person);
}
qi::rule<Iterator, ast::records(), Skipper> _start;
qi::rule<Iterator, ast::person(), Skipper> _person;
qi::rule<Iterator, ast::fruit(), Skipper> _fruit;
qi::rule<Iterator, std::string()> _text;
};
int main()
{
typedef std::string::const_iterator It;
std::string const input("(jane, 23000)(david, 19)(mary, 30)[yello,100][green, 60.6][red, 30.5]");
MyGrammar <It> my_grammar;
It begin(input.begin()), end(input.end());
ast::records data;
if (qi::phrase_parse(begin, end, my_grammar, qi::space, data))
{
std::cout << "Parse success\n";
for (auto& r : data)
std::cout << r << "\n";
}
else
std::cout << "Parse failed\n";
if (begin != end)
std::cout << "Remaining unparsed: '" << std::string(begin, end) << "'\n";
}