问题
I’m trying to parse a character string into an attribute of a custom type symbol
, which contains a std::string
member. I thought I could use BOOST_FUSION_ADAPT_STRUCT
here but that doesn’t work.
If I declare the rule as rule<It, std::string(), space_type>
it works. If I define it as rule<It, symbol(), space_type>
it fails with the error “no type name value_type
in symbol
”. I think Spirit is trying to append the value character-for-character to the attribute, which fails as expected. But isn’t there a way of making this work, without adding an additional intermediate rule which captures the std::string
attribute?
Here’s the full MWE:
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
struct symbol
{
std::string repr;
};
BOOST_FUSION_ADAPT_STRUCT(symbol, (std::string, repr))
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct test_grammar : qi::grammar<Iterator, symbol(), qi::ascii::space_type> {
test_grammar() : test_grammar::base_type{start} {
start = qi::lexeme[+qi::char_("a-z")];
}
qi::rule<Iterator, symbol(), qi::ascii::space_type> start;
};
#include <iostream>
auto main() -> int {
test_grammar<std::string::iterator> grammar{};
auto input = std::string{"test"};
auto output = symbol{};
auto e = end(input);
if (qi::phrase_parse(begin(input), e, grammar, qi::ascii::space, output))
std::cout << output.repr;
}
回答1:
As you have seen from the linked dupe, you can get around this with a well-placed qi::eps
.
start = qi::eps >> qi::lexeme[+qi::char_("a-z")];
See it Live On Coliru
However, there are cases where qi::eps
doesn't save. (Will try to find links later). Therefore, I've come to favour the 'old-fashioned approach' for attribute compatibility:
#include <boost/spirit/include/qi.hpp>
struct symbol
{
std::string repr;
symbol(std::string repr = std::string()) : repr(std::move(repr)) {}
};
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct test_grammar : qi::grammar<Iterator, symbol(), qi::ascii::space_type> {
test_grammar() : test_grammar::base_type(start) {
using namespace qi;
start = as_string[ lexeme[ +char_("a-z") ] ];
}
qi::rule<Iterator, symbol(), qi::ascii::space_type> start;
};
#include <iostream>
auto main() -> int {
test_grammar<std::string::iterator> grammar{};
auto input = std::string{"test"};
auto output = symbol{};
auto e = end(input);
if (qi::phrase_parse(begin(input), e, grammar, qi::ascii::space, output))
std::cout << output.repr;
}
This is also likely a bit lighter on the compiler. See it Live on Coliru as well.
If all else fails you can have your cake and eat it too because attribute transformation/assignment are customization points in the library.
- Attribute Propagation and Attribute Compatibility
- Store a Parsed Attribute Value (Qi)
- Store Parsed Attribute Values into a Container (Qi)
- search for these traits in answers on SO for good examples
来源:https://stackoverflow.com/questions/21511731/adapt-class-containing-a-string-member-as-synthesized-attribute