Boost Spirit Qi因违反内存而崩溃

时间:2018-10-28 16:10:26

标签: c++ boost-spirit-qi

但是我不知道为什么...?

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以及在带有also的Coliru上进行了尝试。我看不到错误。

谢谢。

1 个答案:

答案 0 :(得分:1)

问题在于使用auto表达式来捕获规则,这从解析器表达式中推导了类型。该类型是原型表达式树,它通过引用捕获任何关系,但是这意味着许多_中间体在封闭的full-expresion(请参见C++: Life span of temporary arguments?)结束之后消失了。

这是众所周知的,您可以在这里看到:

这是最简单的解决方法:

auto versionParser = bsq::copy(
    bsq::uint_
    >> -('.' >> bsq::uint_)
    >> -('.' >> bsq::uint_)
    >> -('.' >> bsq::uint_));

如果您还修复了缺少的局部变量初始化方法,则它可以正常工作:

Live On Coliru

#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

附加说明

  1. 为避免整体出现“统一属性”的情况,让我们将解析器分配给所有元素,即使在输入文本中未指定该元素也是如此:

        >> ('.' >> bsq::uint_ | bsq::attr(0))
        >> ('.' >> bsq::uint_ | bsq::attr(0))
        >> ('.' >> bsq::uint_ | bsq::attr(0))
    
  2. 要诊断出现尾部“垃圾”的错误(例如使用"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);
    
  3. 由于版本在语义上是元组,为什么不这样表示它?

    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";
    

结合这些:

Live On Coliru

#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很烂?

我同意。因此,等效地编写您自己的结构:

Live On Coliru

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(Getting into boost spirit; Qi or X3?)没有遇到的auto问题:

Live On Coliru

#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";
}

也打印相同。