The following code crashes. I suspect that the grammar cannot be copied.
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_no_case.hpp>
#include <boost/phoenix/bind/bind_function.hpp>
#include <boost/phoenix/bind/bind_function_object.hpp>
#include <boost/phoenix/operator.hpp>
namespace test
{
namespace qi = boost::spirit::qi;
template<typename IT>
auto make_keyword_parser(const char *const _p, const bool _b)
{ return (qi::eps(_b) >> qi::lit(_p)) | (qi::eps(!_b) >> qi::no_case[qi::lit(_p)]);
}
template<typename IT>
struct booleanParser:qi::grammar<IT, bool(), qi::space_type>
{ qi::rule<IT, bool(), qi::space_type> m_sStart;
booleanParser(const bool _b)
:booleanParser::base_type(m_sStart, "booleanParser")
{ m_sStart = make_keyword_parser<IT>("true", _b)[qi::_val = true]
| make_keyword_parser<IT>("false", _b)[qi::_val = false];
}
};
}
int main()
{
namespace qi = boost::spirit::qi;
test::booleanParser<const char*> sGrammar(false);
bool b;
static constexpr char ac[] = "True";
auto p = ac;
if (!boost::spirit::qi::phrase_parse(p, ac + sizeof ac - 1, sGrammar, qi::space_type(), b) || p != ac + sizeof ac - 1)
std::cerr << "error" << std::endl;
else
std::cerr << "b=" << b << std::endl;
}
The code works fine, if the body of the make_keyword_parser()
function consists only of return qi::lit(_p);
.
It looks like your post is mostly code; please add some more details. It looks like your post is mostly code; please add some more details. It looks like your post is mostly code; please add some more details.
Without reading your code, I've treated multiple very similar questions in the past.
The TL;DR is "Here be dragons" as it is very easy to accidentally invoke Undefined Behaviour due the fact that Phoenix expressions are not meant to be named, so liberally contain references to temporaries.
Here a meta answer that might interest you in particular Boost.Spirit: Setup sub-grammar during parsing which links to the following examples
in boost::spirit::qi, is it possible to dynamically modify rule definition in runtime
Boost.Spirit.Qi crashes when assigning rule to a sequence including itself
Practically all these posts will spend some time avoiding the UB traps, so caveat emptor.
Now let me read the question code and see whether I can add something specific to that.
Indeed your code also suffered the UB. You can avoid it by requesting a deep-copy of your phoenix-expression when you return it:
return qi::copy((qi::eps(_b) >> _p) | (qi::eps(!_b) >> qi::no_case[_p]));
qi::copy
is just a convenience shorthand for boost::proto::deep_copy
.
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
using namespace std::string_view_literals;
namespace qi = boost::spirit::qi;
namespace test {
template <typename IT> auto make_keyword_parser(char const* const _p, bool const _b) {
return qi::copy((qi::eps(_b) >> _p) | (qi::eps(!_b) >> qi::no_case[_p]));
}
template <typename It> struct booleanParser : qi::grammar<It, bool()> {
booleanParser(bool const _b) : booleanParser::base_type(m_sStart, "booleanParser") {
m_sStart = make_keyword_parser<It>("true", _b)[qi::_val = true] |
make_keyword_parser<It>("false", _b)[qi::_val = false];
}
private:
qi::rule<It, bool()> m_sStart;
};
} // namespace test
int main() {
for (bool opt : {true, false}) {
std::cout << " --- Opt: " << std::boolalpha << opt << std::endl;
test::booleanParser<char const*> sGrammar(opt);
static constexpr auto txt = "True"sv;
if (bool b; parse(begin(txt), end(txt), sGrammar >> qi::eoi, b))
std::cout << "b=" << b << std::endl;
else
std::cout << "error" << std::endl;
}
}
Prints
--- Opt: true
error
--- Opt: false
b=true
I'd be remiss if I didn't make a few remarks:
don't expose the skipper. The caller has no business overriding your skipper, it belongs to the grammar
don't use a skipper on a lexeme (boolParser is a lexeme)
don't check of iterators after the fact, specify qi::eoi
in the parser
Your make_keyword_parser
does a few strange things:
it doesn't create a keyword parser (a keyword parser would logically check that the end of the match is on a word/identifier boundary). I've shown you before how to do it (commenting that particular purpose in the code). Probably on more than one occasion (I forget) but certainly in my last answer to you :)
it creates a dynamica rule, where the _b
argument isn't dynamic. Consider just returning a static rule, something like
template <typename Attr = qi::unused_type, typename It>
auto const& make_keyword_parser(char const* const _p, bool const _b) {
static const qi::rule<It, Attr()> //
pcs = _p, //
pci = qi::no_case[_p];
return _b ? pcs : pci;
}
That way you don't repeatedly pay runtime cost.
In fact creating an entire grammar<>
around a single rule
just adds overhead and no features. I'd do away with the grammar (which adds debugging transparency by not going through a rule named "m_sStart". Instead you can name it "booleanValue"
or something
Prefer qi::symbols<char, bool>
; it will be more efficient and less error prone (e.g. when several options in the mapping can share prefixes)
If you don't want that, consider qi::attr(x)
instead of semantic-action [ _val = x ]
. This is particularly nifty with qi::match
I think that's about it for now.