Boost Spirit Qi 因内存违规而崩溃
Boost Spirit Qi crashes for memory violation
但我不明白为什么...?
http://coliru.stacked-crooked.com/a/2912593bb421a35e
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace bsq = boost::spirit::qi;
int main()
{
std::uint16_t major, minor, build, revision;
auto versionParser =
bsq::uint_
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_);
std::string version = "3.5.1";
auto start = version.begin();
if (!bsq::parse(start, version.end(), versionParser, major, minor, build, revision))
{
std::cout << "Error!\n";
}
std::cout << major << "-" << minor << "-" << build << "-" << revision << std::endl;
return 0;
}
调用 parse()
导致内存访问冲突。
我发誓我曾经有过这个工作但是......也许我在做白日梦。我在 Windows 和 Visual Studio 2017 上试过,也在 Coliru 上用 clang 试过。我看不到错误。
谢谢。
问题是使用 auto
表达式来捕获规则,它从解析器表达式中推导出类型。该类型是原型表达式树,它通过引用捕获任何关系,但这意味着许多 _the 中间体在封闭 full-expresion (see C++: Life span of temporary arguments?).
结束后消失了
这是众所周知的,正如您在此处看到的那样:
- Assigning parsers to auto variables
- boost spirit V2 qi bug associated with optimization level
- undefined behaviour somewhere in boost::spirit::qi::phrase_parse
- 还有一些
这是最简单的解决方法:
auto versionParser = bsq::copy(
bsq::uint_
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_));
如果您还修复了 缺少的局部变量初始化,它会正常工作:
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace bsq = boost::spirit::qi;
int main()
{
std::cout << "BOOST_VERSION: " << BOOST_VERSION << std::endl;
std::uint16_t major = 0, minor = 0, build = 0, revision = 0;
auto versionParser = bsq::copy(
bsq::uint_
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_));
std::string version = "3.5.1";
auto start = version.begin();
if (!bsq::parse(start, version.end(), versionParser, major, minor, build, revision))
{
std::cout << "Error!\n";
}
std::cout << major << "-" << minor << "-" << build << "-" << revision << std::endl;
}
版画
BOOST_VERSION: 106600
3-5-1-0
补充说明
为了避免整个 "unitialized attribute" 的情况,让解析器分配给所有元素,即使在输入文本中未指定:
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
要诊断尾随 "garbage" 的错误(与 "3.4bogus"
一样),您可以添加检查是否已解析完整输入:
auto versionParser = bsq::copy(
bsq::uint_
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> bsq::eoi);
因为版本在语义上是一个元组,为什么不这样表示呢?
using Version = std::tuple<uint16_t, uint16_t, uint16_t, uint16_t>;
Version parsed;
if (!bsq::parse(version.begin(), version.end(), versionParser, parsed))
std::cout << "Error!\n";
这样你甚至可以说:
using boost::fusion::operator<<;
auto obsolete = parsed < Version(3, 4, 0, 0);
std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";
结合这些:
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/spirit/include/qi.hpp>
namespace bsq = boost::spirit::qi;
int main() {
auto versionParser = bsq::copy(
bsq::uint_
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> bsq::eoi);
std::string version = "3.5.1";
using Version = std::tuple<uint16_t, uint16_t, uint16_t, uint16_t>;
Version parsed;
if (!bsq::parse(version.begin(), version.end(), versionParser, parsed))
std::cout << "Error!\n";
using boost::fusion::operator<<;
auto obsolete = parsed < Version(3, 4, 0, 0);
std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";
}
版画
Version (3 5 1 0) supported
std::tuple
糟透了?
我同意。因此,等效地编写您自己的结构:
struct Version {
uint16_t major, minor, revision, build;
auto key() const { return std::tie(major, minor, revision, build); }
bool operator<(Version const& b) const { return key() < b.key(); }
};
BOOST_FUSION_ADAPT_STRUCT(Version, major, minor, revision, build)
与时俱进
请注意,Spirit X3 () 没有您 运行 遇到的 auto
问题:
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
namespace bsx = boost::spirit::x3;
struct Version {
uint16_t major, minor, revision, build;
auto key() const { return std::tie(major, minor, revision, build); }
bool operator<(Version const& b) const { return key() < b.key(); }
};
BOOST_FUSION_ADAPT_STRUCT(Version, major, minor, revision, build)
int main() {
auto versionParser = bsx::uint_
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> bsx::eoi;
std::string version = "3.5.1";
Version parsed;
if (!parse(version.begin(), version.end(), versionParser, parsed))
std::cout << "Error!\n";
using boost::fusion::operator<<;
auto obsolete = parsed < Version{3, 4, 0, 0};
std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";
}
同样打印。
但我不明白为什么...?
http://coliru.stacked-crooked.com/a/2912593bb421a35e
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace bsq = boost::spirit::qi;
int main()
{
std::uint16_t major, minor, build, revision;
auto versionParser =
bsq::uint_
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_);
std::string version = "3.5.1";
auto start = version.begin();
if (!bsq::parse(start, version.end(), versionParser, major, minor, build, revision))
{
std::cout << "Error!\n";
}
std::cout << major << "-" << minor << "-" << build << "-" << revision << std::endl;
return 0;
}
调用 parse()
导致内存访问冲突。
我发誓我曾经有过这个工作但是......也许我在做白日梦。我在 Windows 和 Visual Studio 2017 上试过,也在 Coliru 上用 clang 试过。我看不到错误。
谢谢。
问题是使用 auto
表达式来捕获规则,它从解析器表达式中推导出类型。该类型是原型表达式树,它通过引用捕获任何关系,但这意味着许多 _the 中间体在封闭 full-expresion (see C++: Life span of temporary arguments?).
这是众所周知的,正如您在此处看到的那样:
- Assigning parsers to auto variables
- boost spirit V2 qi bug associated with optimization level
- undefined behaviour somewhere in boost::spirit::qi::phrase_parse
- 还有一些
这是最简单的解决方法:
auto versionParser = bsq::copy(
bsq::uint_
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_));
如果您还修复了 缺少的局部变量初始化,它会正常工作:
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace bsq = boost::spirit::qi;
int main()
{
std::cout << "BOOST_VERSION: " << BOOST_VERSION << std::endl;
std::uint16_t major = 0, minor = 0, build = 0, revision = 0;
auto versionParser = bsq::copy(
bsq::uint_
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_));
std::string version = "3.5.1";
auto start = version.begin();
if (!bsq::parse(start, version.end(), versionParser, major, minor, build, revision))
{
std::cout << "Error!\n";
}
std::cout << major << "-" << minor << "-" << build << "-" << revision << std::endl;
}
版画
BOOST_VERSION: 106600
3-5-1-0
补充说明
为了避免整个 "unitialized attribute" 的情况,让解析器分配给所有元素,即使在输入文本中未指定:
>> ('.' >> bsq::uint_ | bsq::attr(0)) >> ('.' >> bsq::uint_ | bsq::attr(0)) >> ('.' >> bsq::uint_ | bsq::attr(0))
要诊断尾随 "garbage" 的错误(与
"3.4bogus"
一样),您可以添加检查是否已解析完整输入:auto versionParser = bsq::copy( bsq::uint_ >> ('.' >> bsq::uint_ | bsq::attr(0)) >> ('.' >> bsq::uint_ | bsq::attr(0)) >> ('.' >> bsq::uint_ | bsq::attr(0)) >> bsq::eoi);
因为版本在语义上是一个元组,为什么不这样表示呢?
using Version = std::tuple<uint16_t, uint16_t, uint16_t, uint16_t>; Version parsed; if (!bsq::parse(version.begin(), version.end(), versionParser, parsed)) std::cout << "Error!\n";
这样你甚至可以说:
using boost::fusion::operator<<; auto obsolete = parsed < Version(3, 4, 0, 0); std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";
结合这些:
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/spirit/include/qi.hpp>
namespace bsq = boost::spirit::qi;
int main() {
auto versionParser = bsq::copy(
bsq::uint_
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> bsq::eoi);
std::string version = "3.5.1";
using Version = std::tuple<uint16_t, uint16_t, uint16_t, uint16_t>;
Version parsed;
if (!bsq::parse(version.begin(), version.end(), versionParser, parsed))
std::cout << "Error!\n";
using boost::fusion::operator<<;
auto obsolete = parsed < Version(3, 4, 0, 0);
std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";
}
版画
Version (3 5 1 0) supported
std::tuple
糟透了?
我同意。因此,等效地编写您自己的结构:
struct Version {
uint16_t major, minor, revision, build;
auto key() const { return std::tie(major, minor, revision, build); }
bool operator<(Version const& b) const { return key() < b.key(); }
};
BOOST_FUSION_ADAPT_STRUCT(Version, major, minor, revision, build)
与时俱进
请注意,Spirit X3 (auto
问题:
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
namespace bsx = boost::spirit::x3;
struct Version {
uint16_t major, minor, revision, build;
auto key() const { return std::tie(major, minor, revision, build); }
bool operator<(Version const& b) const { return key() < b.key(); }
};
BOOST_FUSION_ADAPT_STRUCT(Version, major, minor, revision, build)
int main() {
auto versionParser = bsx::uint_
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> bsx::eoi;
std::string version = "3.5.1";
Version parsed;
if (!parse(version.begin(), version.end(), versionParser, parsed))
std::cout << "Error!\n";
using boost::fusion::operator<<;
auto obsolete = parsed < Version{3, 4, 0, 0};
std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";
}
同样打印。