在提升::精神::lex 中的访问冲突

Access violation in boost::spirit::lex

本文关键字:访问冲突 lex 精神      更新时间:2023-10-16

我已经将代码减少到重现错误所需的绝对最小值(可悲的是,这仍然是 60 行,不是很 Minimal,但至少是它的 VCE(。

我在Visual Studio 2013(Platform Toolset v120(中使用Boost 1.56。

下面的代码给了我一个访问冲突,除非我取消注释标记的行。通过做一些测试,如果枚举从 0 开始,似乎 boost::spirit 不喜欢它(在我的完整代码中,我在枚举中有更多的值,我只是设置了IntLiteral = 1,它也摆脱了访问违规错误,尽管名称是错误的,因为 ToString 在索引到数组时偏离了一个(。

这是 boost::spirit 中的错误还是我做错了什么?

#include <iostream>
#include <string>
#include <vector>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
namespace lex = boost::spirit::lex;
typedef lex::lexertl::token<char const*> LexToken;
typedef lex::lexertl::actor_lexer<LexToken> LexerType;
typedef boost::iterator_range<char const*> IteratorRange;
enum TokenType
{
    //Unused, // <-- Uncommenting this fixes the error (1/2)
    IntLiteral,
};
std::string tokenTypeNames[] = {
    //"unused", // <-- Uncommenting this line fixes the error (2/2)
    "int literal",
};
std::string ToString(TokenType t)
{
    return tokenTypeNames[t];
}
template <typename T>
struct Lexer : boost::spirit::lex::lexer < T >
{
    Lexer()
    {
        self.add
            // Literals
            ("[1-9][0-9]*", TokenType::IntLiteral);
    }
};
int main(int argc, char* argv[])
{
    std::cout << "Boost version: " << BOOST_LIB_VERSION << std::endl;
    std::string input = "33";
    char const* inputIt = input.c_str();
    char const* inputEnd = &input[input.size()];
    Lexer<LexerType> tokens;
    LexerType::iterator_type token = tokens.begin(inputIt, inputEnd);
    LexerType::iterator_type end = tokens.end();
    for (; token->is_valid() && token != end; ++token)
    {
        auto range = boost::get<IteratorRange>(token->value());
        std::cout << ToString(static_cast<TokenType>(token->id())) << " (" << std::string(range.begin(), range.end()) << ')' << std::endl;
    }
    std::cin.get();
    return 0;
}

如果我取消注释我得到的行:

Boost version: 1_56
int literal (33)

如果你取消注释这些行,它"有效"的事实纯粹是偶然的。

从文档中 spirit/lex/tutorials/lexer_quickstart2.html:

为了确保每个令牌都被分配一个id,Spirit.Lex库在内部为令牌定义分配了唯一的编号,从boost::spirit::lex::min_token_id定义的常量开始

另请参阅此较旧的答案:

  • Spirit Lex:哪个令牌定义生成了此令牌?

因此,您可以使用偏移量来修复它,但我想它将继续是一个脆弱的解决方案,因为很容易让枚举与词法分析器表中的实际令牌定义不同步。

我建议使用链接答案中给出的nameof()方法,该方法利用命名token_def<>对象。

住在科里鲁

#include <iostream>
#include <string>
#include <vector>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
namespace lex = boost::spirit::lex;
typedef lex::lexertl::token<char const*> LexToken;
typedef lex::lexertl::actor_lexer<LexToken> LexerType;
typedef boost::iterator_range<char const*> IteratorRange;
enum TokenType {
    IntLiteral = boost::spirit::lex::min_token_id
};
std::string const& ToString(TokenType t) {
    static const std::string tokenTypeNames[] = {
        "int literal",
    };
    return tokenTypeNames[t - boost::spirit::lex::min_token_id];
}
template <typename T>
struct Lexer : boost::spirit::lex::lexer<T> {
    Lexer() {
        this->self.add
            // Literals
            ("[1-9][0-9]*", TokenType::IntLiteral);
    }
};
int main() {
    std::cout << "Boost version: " << BOOST_LIB_VERSION << std::endl;
    std::string input = "33";
    char const* inputIt = input.c_str();
    char const* inputEnd = &input[input.size()];
    Lexer<LexerType> tokens;
    LexerType::iterator_type token = tokens.begin(inputIt, inputEnd);
    LexerType::iterator_type end = tokens.end();
    for (; token->is_valid() && token != end; ++token)
    {
        auto range = boost::get<IteratorRange>(token->value());
        std::cout << ToString(static_cast<TokenType>(token->id())) << " (" << std::string(range.begin(), range.end()) << ')' << std::endl;
    }
}