在 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
现场演示
扩展一点并显示一些更微妙的模式:
#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 的示例:
#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"
我使用 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
现场演示
扩展一点并显示一些更微妙的模式:
#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 的示例:
#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"