在 boost::qi 中使用太多替代运算符导致分段错误

Using too many alternative operator in boost::qi caused segmentation fault

我使用 qi::spirit 生成了一个简单的代码:

#include <boost/spirit/include/qi.hpp>
#include <string>

using namespace std;
using namespace boost::spirit;
int main() {
    string str = "string";
    auto begin = str.begin();
    auto symbols = (qi::lit(";") | qi::lit("(") | qi::lit(")") | qi::lit("+") |
                    qi::lit("/") | qi::lit("-") | qi::lit("*"));
    qi::parse(begin, str.end(), *(qi::char_ - symbols));
}

然后这个程序被SEGV.Then终止了, 我重写的代码在符号的 rhs 中使用较少的替代运算符,

#include <boost/spirit/include/qi.hpp>
#include <string>
using namespace std;
using namespace boost::spirit;
int main()
{
    string str = "string";
    auto begin = str.begin();
    auto symbols = (qi::lit(";") | qi::lit("+") | qi::lit("/") | qi::lit("-") |
            qi::lit("*"));
    qi::parse(begin, str.end(), *(qi::char_ - symbols));
}

现在运行良好。 2种情况有什么区别?

问题是使用 ( 并用双引号将其括起来并不是很安全,它可能会被完全解释为其他东西,您可能想尝试使用 / 来转义它。

你的问题是一个典型的错误:使用auto来存储Qi解析器表达式:Assigning parsers to auto variables

这导致 UB

  • 使用规则,或者qi::copy(hooed下就是proto::deep_copy)。

    auto symbols = qi::copy(qi::lit(";") | qi::lit("(") | qi::lit(")") | qi::lit("+") |
         qi::lit("/") | qi::lit("-") | qi::lit("*"));
    
  • 更好的是,使用字符集一次匹配所有字符,

    auto symbols = qi::copy(qi::omit(qi::char_(";()+/*-")));
    

    omit[] 抵消了 char_ 公开其属性的事实(lit 不公开)。但是因为你曾经做过的只是从另一个字符集中减去:

    qi::char_ - symbols
    

    你也可以直接写

    qi::char_ - qi::char_(";()+/*-")
    

    现在。你可能不知道,但你可以用 ~charset 来否定它,所以它会变成

    ~qi::char_(";()+/*-")
    

    NOTE - can have special meaning in charsets, which is why I very subtly move it to the end. See docs

现场演示

扩展一点并显示一些更微妙的模式:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <string>

using namespace std;
using namespace boost::spirit;
int main() {
    string const str = "string;some(thing) else + http://me@host:*/path-element.php";
    
    auto cs = ";()+/*-";
    using qi::char_;

    {
        std::vector<std::string> tokens;
        qi::parse(str.begin(), str.end(), +~char_(cs) % +char_(cs), tokens);

        std::cout << "Condensing: ";
        for (auto& tok : tokens) {
            std::cout << " " << std::quoted(tok);
        }
        std::cout << std::endl;
    }

    {
        std::vector<std::string> tokens;
        qi::parse(str.begin(), str.end(), *~char_(cs) % char_(cs), tokens);

        std::cout << "Not condensing: ";
        for (auto& tok : tokens) {
            std::cout << " " << std::quoted(tok);
        }
        std::cout << std::endl;
    }
}

版画

Condensing:  "string" "some" "thing" " else " " http:" "me@host:" "path" "element.php"
Not condensing:  "string" "some" "thing" " else " " http:" "" "me@host:" "" "path" "element.php"

X3

如果你有 c++14,你可以使用 Spirit X3,它没有“自动问题”(因为它没有可以获取悬挂引用的 Proto 表达式树)。

您的原始代码 would have been fine in X3,编译速度会快很多。

这是我使用 X3 的示例:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>
#include <string>

namespace x3 = boost::spirit::x3;
int main() {
    std::string const str = "string;some(thing) else + http://me@host:*/path-element.php";
    auto const cs = x3::char_(";()+/*-");

    std::vector<std::string> tokens;
    x3::parse(str.begin(), str.end(), +~cs % +cs, tokens);
    //x3::parse(str.begin(), str.end(), *~cs % cs, tokens);

    for (auto& tok : tokens) {
        std::cout << " " << std::quoted(tok);
    }
}

打印

 "string" "some" "thing" " else " " http:" "me@host:" "path" "element.php"