boost spirit语法的不一致行为
inconsistent behavior of boost spirit grammar
我有一些语法要用于工作项目。最小可执行示例是:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#pragma GCC diagnostic ignored "-Wunused-variable"
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#pragma GCC diagnostic pop // pops
#include <iostream>
int main()
{
typedef unsigned long long ull;
std::string curline = "1;2;;3,4;5";
std::cout << "parsing: " << curline << "\n";
namespace qi = boost::spirit::qi;
auto ids = -qi::ulong_long % ','; // '-' allows for empty vecs.
auto match_type_res = ids % ';' ;
std::vector<std::vector<ull> > r;
qi::parse(curline.begin(), curline.end(), match_type_res, r);
std::cout << "got: ";
for (auto v: r){
for (auto i : v)
std::cout << i << ",";
std::cout << ";";
}
std::cout <<"\n";
}
在我的个人机器上,这会产生正确的输出:
解析:1;2;;3,4;5
得到:1,;2,;;3,4,;5,;
但在工作中它会产生:
解析:1;2;;3,4;5
得到:1,;2,;;3,
换句话说,一旦其中有多个元素,它就无法解析长整数向量。
现在,我确定工作系统使用的是boost 1.56,而我的私人计算机使用的是1.57。这是原因吗?
知道我们这里有一些真正的堆栈溢出专家,我希望有人知道这个问题的来源,或者至少可以缩小我需要检查的范围。
如果 boost 版本有问题,我可能会说服公司升级,但无论如何都欢迎提供解决方法。
您正在代码中调用 Undefined Behaviour。
特别是在您使用 auto
存储解析器表达式的地方。包含完整表达式 ¹ 的 末尾的 Expression Template contains references to temporaries that become dangling。
UB 意味着任何事情都可能发生。两个编译器都是对的!最好的部分是,您可能会看到不同的行为,具体取决于所使用的编译器标志。
使用以下方法修复它:
qi::copy
(或 v.1.55 IIRC 之前的 boost::proto::deep_copy
)
- 使用
BOOST_SPIRIT_AUTO
而不是 BOOST_AUTO
(如果您还支持 C++03,则最有帮助)
使用qi::rule<>
和qi::grammar<>
(non-terminals)来键入擦除和表达式。这也会对性能产生影响,但也会提供更多功能,例如
- 递归规则
- 本地和继承属性
- 声明的船长(方便,因为规则可以隐式
lexeme[]
(参见 here)
- 更好的代码组织。
Note also that Spirit X3 promises to drop there restrictions on the use with auto. It's basically a whole lot more lightweight due to the use of c++14 features. Keep in mind that it's not stable yet.
显示带有 -O2 的 GCC 显示未定义的结果; Live On Coliru
固定版本:
//#pragma GCC diagnostic push
//#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
//#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
//#pragma GCC diagnostic ignored "-Wunused-variable"
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi.hpp>
//#pragma GCC diagnostic pop // pops
#include <iostream>
int main() {
typedef unsigned long long ull;
std::string const curline = "1;2;;3,4;5";
std::cout << "parsing: '" << curline << "'\n";
namespace qi = boost::spirit::qi;
#if 0 // THIS IS UNDEFINED BEHAVIOUR:
auto ids = -qi::ulong_long % ','; // '-' allows for empty vecs.
auto grammar = ids % ';';
#else // THIS IS CORRECT:
auto ids = qi::copy(-qi::ulong_long % ','); // '-' allows for empty vecs.
auto grammar = qi::copy(ids % ';');
#endif
std::vector<std::vector<ull> > r;
qi::parse(curline.begin(), curline.end(), grammar, r);
std::cout << "got: ";
for (auto v: r){
for (auto i : v)
std::cout << i << ",";
std::cout << ";";
}
std::cout <<"\n";
}
打印(也使用 GCC -O2!):
parsing: '1;2;;3,4;5'
got: 1,;2,;;3,4,;5,;
¹(这里基本上是 "at the next semicolon";但在标准语中)
我有一些语法要用于工作项目。最小可执行示例是:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#pragma GCC diagnostic ignored "-Wunused-variable"
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#pragma GCC diagnostic pop // pops
#include <iostream>
int main()
{
typedef unsigned long long ull;
std::string curline = "1;2;;3,4;5";
std::cout << "parsing: " << curline << "\n";
namespace qi = boost::spirit::qi;
auto ids = -qi::ulong_long % ','; // '-' allows for empty vecs.
auto match_type_res = ids % ';' ;
std::vector<std::vector<ull> > r;
qi::parse(curline.begin(), curline.end(), match_type_res, r);
std::cout << "got: ";
for (auto v: r){
for (auto i : v)
std::cout << i << ",";
std::cout << ";";
}
std::cout <<"\n";
}
在我的个人机器上,这会产生正确的输出: 解析:1;2;;3,4;5 得到:1,;2,;;3,4,;5,;
但在工作中它会产生: 解析:1;2;;3,4;5 得到:1,;2,;;3,
换句话说,一旦其中有多个元素,它就无法解析长整数向量。
现在,我确定工作系统使用的是boost 1.56,而我的私人计算机使用的是1.57。这是原因吗?
知道我们这里有一些真正的堆栈溢出专家,我希望有人知道这个问题的来源,或者至少可以缩小我需要检查的范围。
如果 boost 版本有问题,我可能会说服公司升级,但无论如何都欢迎提供解决方法。
您正在代码中调用 Undefined Behaviour。
特别是在您使用 auto
存储解析器表达式的地方。包含完整表达式 ¹ 的 末尾的 Expression Template contains references to temporaries that become dangling。
UB 意味着任何事情都可能发生。两个编译器都是对的!最好的部分是,您可能会看到不同的行为,具体取决于所使用的编译器标志。
使用以下方法修复它:
qi::copy
(或 v.1.55 IIRC 之前的boost::proto::deep_copy
)- 使用
BOOST_SPIRIT_AUTO
而不是BOOST_AUTO
(如果您还支持 C++03,则最有帮助) 使用
qi::rule<>
和qi::grammar<>
(non-terminals)来键入擦除和表达式。这也会对性能产生影响,但也会提供更多功能,例如- 递归规则
- 本地和继承属性
- 声明的船长(方便,因为规则可以隐式
lexeme[]
(参见 here) - 更好的代码组织。
Note also that Spirit X3 promises to drop there restrictions on the use with auto. It's basically a whole lot more lightweight due to the use of c++14 features. Keep in mind that it's not stable yet.
显示带有 -O2 的 GCC 显示未定义的结果; Live On Coliru
固定版本:
//#pragma GCC diagnostic push
//#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
//#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
//#pragma GCC diagnostic ignored "-Wunused-variable"
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi.hpp>
//#pragma GCC diagnostic pop // pops
#include <iostream>
int main() {
typedef unsigned long long ull;
std::string const curline = "1;2;;3,4;5";
std::cout << "parsing: '" << curline << "'\n";
namespace qi = boost::spirit::qi;
#if 0 // THIS IS UNDEFINED BEHAVIOUR:
auto ids = -qi::ulong_long % ','; // '-' allows for empty vecs.
auto grammar = ids % ';';
#else // THIS IS CORRECT:
auto ids = qi::copy(-qi::ulong_long % ','); // '-' allows for empty vecs.
auto grammar = qi::copy(ids % ';');
#endif
std::vector<std::vector<ull> > r;
qi::parse(curline.begin(), curline.end(), grammar, r);
std::cout << "got: ";
for (auto v: r){
for (auto i : v)
std::cout << i << ",";
std::cout << ";";
}
std::cout <<"\n";
}
打印(也使用 GCC -O2!):
parsing: '1;2;;3,4;5'
got: 1,;2,;;3,4,;5,;
¹(这里基本上是 "at the next semicolon";但在标准语中)