boost :: spirit :: lex中的访问冲突

时间:2014-12-18 02:17:08

标签: c++ boost-spirit

我已将代码缩减到重现错误所需的绝对最小值(遗憾的是仍然是60行,不是很小,但至少是它的VCE)。

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

下面的代码为我提供了访问冲突,除非我取消注释标记的行。通过做一些测试,似乎boost :: spirit不喜欢它,如果枚举从0开始(在我的完整代码中我在枚举中有更多的值,我只是设置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)

1 个答案:

答案 0 :(得分:3)

它&#34;工作&#34;如果你取消注释这些线,那是纯粹的意外。

来自文档spirit/lex/tutorials/lexer_quickstart2.html

  

为确保为每个令牌分配一个id,Spirit.Lex库在内部为令牌定义分配唯一的数字,从boost::spirit::lex::min_token_id

定义的常量开始

另见这个较旧的答案:

所以你可以使用偏移来修复它,但我想它会继续成为一个脆弱的解决方案,因为很容易让枚举与lexer表中的实际令牌定义不同步。

我建议使用the linked answer中给出的nameof()方法,该方法利用了名为token_def<>的对象。

<强> Live On Coliru

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