c++ - 在 boost-spirit 中,有没有办法通过函数调用创建语法以减少代码?

标签 c++ boost-spirit

以下代码崩溃。我怀疑语法无法复制。

#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 表达式不打算命名,因此很容易意外调用未定义行为,因此大量包含对临时变量的引用。

实际上所有这些帖子都会花一些时间来避免 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/

相关文章:

c++ - Boost.Spirit、Nabialek 技巧和错误处理

c++ - Boost::Spirit:优化表达式解析器

c++ - 如何在复制构造函数中调用重载的下标?

c++ - 逗号运算符返回未更改的值

C++ 错误处理——示例代码的良好来源?

c++ - Boost.Signals 弃用

c++ - Boost Spirit : Error C2664, 无法将 'const boost::phoenix::actor<Eval>' 转换为 'char'

c++ - Boost 是否因为不像 Boost 那样有罪?

c++ - Spirit V2 和 X3 的状态

c++ - 如何让用户输入声明字符串数组的大小?