The most natural approach would be to just use a different parser for your more constrained occasions.
Also, if performance is so important that you can't even spare 3 or 4 extra character comparisons
- you should probably do a handwritten parser (in general, Spirit's automatic attribute handling isn't always optimal) or
- consider statically optimized scanning (so Boost Xpressive or Spirit Lex)
- you might perhaps be doing something wrong (if you have a suboptimal grammar you can have much backtracking kill the performance - much like degenerate Regular Expressions (https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS))
That said, here are some options:
1. Use a variable rule through qi::lazy
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
int main()
{
typedef std::string::const_iterator It;
qi::rule<It>
request = "request",
response = "response",
status = "status",
query = "query",
//
allowed;
static qi::rule<It> const start = qi::lazy(phx::ref(allowed)) >> qi::lit("\r\n");
static const auto test = [](std::string const& input) { return qi::parse(begin(input), end(input), start); };
for (int i=0; i<10; ++i)
{
switch(rand()%3)
{
case 0: allowed = request; break;
case 1: allowed = request | response | query; break;
case 2: allowed = request | response | status | query; break;
}
std::cout << "status: " << test("status\r\n") << "\t"
<< "response: " << test("response\r\n") << "\t"
<< "request: " << test("request\r\n") << "\n";
}
}
Like Mike mentions, this is also used in the Nabialek trick, although qi::lazy
is the essential ingredient here.
This prints, e.g.:
status: 0 response: 1 request: 1
status: 0 response: 1 request: 1
status: 0 response: 0 request: 1
status: 0 response: 1 request: 1
status: 1 response: 1 request: 1
status: 0 response: 1 request: 1
status: 0 response: 1 request: 1
status: 0 response: 0 request: 1
status: 0 response: 0 request: 1
status: 0 response: 1 request: 1
2. Use inherited attributes with qi::lazy
Pretty similar to the above, you could pass 'subrules' as inherited attributes. I'm not sure I'd recommend this, as I've seen Undefined Behaviour crop up in past examples, see e.g.
3. Just use separate rules
This is most natural, in my opinion:
std::function<bool(string)> test;
switch(rand()%3)
{
case 0: test = [&](std::string const& input) { return qi::parse(begin(input), end(input), request); }; break;
case 1: test = [&](std::string const& input) { return qi::parse(begin(input), end(input), request | response | query); }; break;
case 2: test = [&](std::string const& input) { return qi::parse(begin(input), end(input), request | response | status | query); }; break;
}
See full sample: http://coliru.stacked-crooked.com/a/603f093add6b9799