以下代码崩溃。我怀疑语法无法复制。
#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;
}
如果 make_keyword_parser()
函数的主体仅包含 return qi::lit(_p);
,则代码可以正常工作。
看起来你的帖子主要是代码;请添加更多详细信息。 看起来你的帖子主要是代码;请添加更多详细信息。 看起来你的帖子主要是代码;请添加更多详细信息。
最佳答案
在没有阅读您的代码的情况下,我过去处理过多个非常相似的问题。
TL;DR 是“这里有龙”,因为 Phoenix 表达式不打算命名,因此很容易意外调用未定义行为,因此大量包含对临时变量的引用。
这是您可能特别感兴趣的元答案 Boost.Spirit: Setup sub-grammar during parsing链接到以下示例
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
实际上所有这些帖子都会花一些时间来避免 UB 陷阱,所以买者自负。
现在让我阅读问题代码,看看是否可以添加一些特定的内容。
评论
确实你的代码也遭受了UB的困扰。您可以通过在返回 phoenix-expression 时请求深度拷贝来避免这种情况:
return qi::copy((qi::eps(_b) >> _p) | (qi::eps(!_b) >> qi::no_case[_p]));
qi::copy
只是 boost::proto::deep_copy
的方便简写.
<强> Live On Coliru
#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;
}
}
打印
--- Opt: true
error
--- Opt: false
b=true
如果我不发表一些评论,那就是我的失职:
不要暴露船长。调用者没有权利覆盖你的船长,它属于语法
不要在词位上使用船长(boolParser 是一个词位)
事后不检查迭代器,指定
qi::eoi
在解析器中您的
make_keyword_parser
做了一些奇怪的事情:它不会创建关键字解析器(关键字解析器会在逻辑上检查匹配的结尾是否位于单词/标识符边界上)。我之前已经向您展示了如何执行此操作(在代码中注释该特定目的)。可能不止一次(我忘了),但肯定是在我给你的最后一个回答中:)
它创建一个动态规则,其中
_b
论证不是动态的。考虑只返回静态规则,例如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; }
这样您就不会重复支付运行时成本。
事实上创建了一个完整的
grammar<>
周边单rule
只是增加了开销,没有任何功能。我会取消语法(通过不执行名为“m_sStart”的规则来增加调试透明度。相反,您可以将其命名为"booleanValue"
或其他名称首选
qi::symbols<char, bool>
;它将更加高效且不易出错(例如,当映射中的多个选项可以共享前缀时)如果您不希望这样,请考虑
qi::attr(x)
而不是语义 Action[ _val = x ]
。这对于qi::match
来说特别漂亮。
我想现在就这些了。</p>
关于c++ - 在 boost-spirit 中,有没有办法通过函数调用创建语法以减少代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76575174/