There are several ways :)
- Custom Attribute Properties
- Same thing with semantic actions
- All in semantic actions, at the level of detail
1. Custom attribute properties
The cleanest, IMO will replace the Fusion sequence adaptation ( BOOST_FUSION_ADAPT_STRUCT ) with the container attribute attributes for Spirit:
namespace boost { namespace spirit { namespace traits { template<> struct is_container<ElemParseData, void> : mpl::true_ { }; template<> struct container_value<ElemParseData, void> { typedef boost::variant<float, unsigned int> type; }; template <> struct push_back_container<ElemParseData, std::vector<float>, void> { static bool call(ElemParseData& c, std::vector<float> const& val) { c.verts.insert(c.verts.end(), val.begin(), val.end()); return true; } }; template <> struct push_back_container<ElemParseData, std::vector<unsigned int>, void> { static bool call(ElemParseData& c, std::vector<unsigned int> const& val) { c.idx.insert(c.idx.end(), val.begin(), val.end()); return true; } }; }}}
Without a change in grammar, this simply leads to the same effect. However, now you can change the parser to expect the desired grammar:
vertex = 'v' >> qi::double_ >> qi::double_ >> qi::double_; elements = 'f' >> qi::int_ >> qi::int_ >> qi::int_; start = *(vertex | elements);
And because of hell, the Spirit will βjust knowβ how to embed in ElemParseData . Watch live on Coliru
2. The same with semantic actions
You can associate it with semantic actions:
start = *( vertex [phx::bind(insert, _val, _1)] | elements [phx::bind(insert, _val, _1)] );
With insert member of type inserter :
struct inserter { template <typename,typename> struct result { typedef void type; }; template <typename Attr, typename Vec> void operator()(Attr& attr, Vec const& v) const { dispatch(attr, v); } private: static void dispatch(ElemParseData& data, std::vector<float> vertices) { data.verts.insert(data.verts.end(), vertices.begin(), vertices.end()); } static void dispatch(ElemParseData& data, std::vector<unsigned int> indices) { data.idx.insert(data.idx.end(), indices.begin(), indices.end()); } };
It looks basically the same and it does the same thing: live on Coliru
3. Everything in semantic actions, at the level of detail
This is the only solution that does not require any plumbing, with the possible exception of boost/spirit/include/phoenix.hpp :
struct objGram : qi::grammar<std::string::const_iterator, ElemParseData(), iso8859::space_type> { objGram() : objGram::base_type(start) { using namespace qi; auto add_vertex = phx::push_back(phx::bind(&ElemParseData::verts, _r1), _1); auto add_index = phx::push_back(phx::bind(&ElemParseData::idx, _r1), _1); vertex = 'v' >> double_ [add_vertex] >> double_ [add_vertex] >> double_ [add_vertex]; elements = 'f' >> int_ [add_index] >> int_ [add_index] >> int_ [add_index] ; start = *(vertex(_val) | elements(_val)); } qi::rule<std::string::const_iterator, ElemParseData(), iso8859::space_type> start; qi::rule<std::string::const_iterator, void(ElemParseData&), iso8859::space_type> vertex, elements; } objGrammar;
Note:
- One small advantage here would be that fewer copies of the values
- The disadvantage is that you lose "atomicity" (if the string cannot be analyzed after the second word, say, the second value, they will be irrevocably transferred to the members of
ElemParseData ).
Side note
There is an error in the read loop; prefer simpler options:
std::filebuf fb; if (fb.open("parsetest.txt", std::ios::in)) { ss << &fb; fb.close(); }
Or consider boost::spirit::istream_iterator