I would not complicate matters by inheriting. The composition is often more than enough, and it will not confuse the interface of par-qi.
I developed a small example of how versioning grammar could be used. Suppose the old grammar:
template <typename It, typename Skipper> struct OldGrammar : qi::grammar<It, Skipper, std::string()> { OldGrammar() : OldGrammar::base_type(mainrule) { using namespace qi; rule1 = int_(1);
As you can see, this was pretty restrictive, requiring the version to be exactly 1. However, the future happened and a new version of the grammar was invented. Now i will add
friend struct NewGrammar<It, Skipper>;
to the old grammar and proceed with the implementation of the new grammar, which kindly returns to the old grammar, if necessary:
template <typename It, typename Skipper> struct NewGrammar : qi::grammar<It, Skipper, std::string()> { NewGrammar() : NewGrammar::base_type(mainrule) { using namespace qi; new_rule1 = int_(2);
(I did not try to get it to work with inheritance, although in all likelihood it should work as well.)
Check out this baby:
template <template <typename It,typename Skipper> class Grammar> bool test(std::string const& input) { auto f(input.begin()), l(input.end()); static const Grammar<std::string::const_iterator, qi::space_type> p; try { return qi::phrase_parse(f,l,p,qi::space) && (f == l); // require full input consumed } catch(...) { return false; } // qi::expectation_failure<> } int main() { assert(true == test<OldGrammar>("version 1 woot")); assert(false == test<OldGrammar>("version 2 nope")); assert(true == test<NewGrammar>("version 1 woot")); assert(true == test<NewGrammar>("version 2 woot as well")); }
All tests pass, obviously: see him live on Coliru 1 Hope this helps!
1 Well, hell. Kolira is too slow to compile it today. So here is the complete test program:
#define BOOST_SPIRIT_DEBUG #include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; template <typename It, typename Skipper> struct NewGrammar; // forward declare for friend declaration template <typename It, typename Skipper> struct OldGrammar : qi::grammar<It, Skipper, std::string()> { friend struct NewGrammar<It, Skipper>; // NOTE OldGrammar() : OldGrammar::base_type(mainrule) { using namespace qi; rule1 = int_(1); // expect version 1 rule2 = *char_; // hopefully some interesting grammar mainrule = omit [ "version" > rule1 ] >> rule2; BOOST_SPIRIT_DEBUG_NODE(mainrule); BOOST_SPIRIT_DEBUG_NODE(rule1); BOOST_SPIRIT_DEBUG_NODE(rule2); } private: qi::rule<It, Skipper, std::string()> mainrule; qi::rule<It, Skipper, int()> rule1; qi::rule<It, Skipper, std::string()> rule2; }; template <typename It, typename Skipper> struct NewGrammar : qi::grammar<It, Skipper, std::string()> { NewGrammar() : NewGrammar::base_type(mainrule) { using namespace qi; new_rule1 = int_(2); // support version 2 now new_start = omit [ "version" >> new_rule1 ] >> old.rule2; // note, no expectation point mainrule = new_start | old.mainrule; // or fall back to version 1 grammar BOOST_SPIRIT_DEBUG_NODE(new_start); BOOST_SPIRIT_DEBUG_NODE(mainrule); BOOST_SPIRIT_DEBUG_NODE(new_rule1); } private: OldGrammar<It, Skipper> old; qi::rule<It, Skipper, std::string()> new_start, mainrule; qi::rule<It, Skipper, int()> new_rule1; }; template <template <typename It,typename Skipper> class Grammar> bool test(std::string const& input) { auto f(input.begin()), l(input.end()); static const Grammar<std::string::const_iterator, qi::space_type> p; try { return qi::phrase_parse(f,l,p,qi::space) && (f == l); // require full input consumed } catch(...) { return false; } // qi::expectation_failure<> } int main() { assert(true == test<OldGrammar>("version 1 woot")); assert(false == test<OldGrammar>("version 2 nope")); assert(true == test<NewGrammar>("version 1 woot")); assert(true == test<NewGrammar>("version 2 woot as well")); }