c++ - std::vector.push_back() throwing badalloc exception -
i started writing lexer, , made following file test far working okay:
main.cpp
#include <iostream> #include "lexer.h" #include "token.h" int main(void) { std::string str(""); // i'll use test expressions lexer lexer(str); std::vector<token> tokens = lexer.lex(); for(auto = tokens.begin(); != tokens.end(); ++it) { std::string str; switch(it->type) { case tokentype::_eof: str = "eof"; break; case tokentype::error: str = "error"; break; case tokentype::semicolon: str = "semicolon"; break; case tokentype::plus: str = "plus"; break; case tokentype::less_than: str = "less_than"; break; case tokentype::greater_than: str = "greater_than"; break; case tokentype::int: str = "int"; break; case tokentype::id: str = "id"; break; case tokentype::whitespace: str = "whitespace"; break; default: str = "<unknown token>"; } std::cout << str << ", detail='" << it->detail << "'" << std::endl; } return 0; }
the line lexer.lex() throws exception. looking in lexer.h
:
std::vector<token> lexer::lex(void) { // reset input pointer inputptr = 0; updatecurrentchar(); // read tokens until eof std::vector<token> tokens; token *token = nullptr; { token = getnext(); tokens.push_back(*token); } while(token->type != tokentype::_eof); return tokens; }
the line tokens.push_back(*token)
throwing exception:
i tried looking @ info on push_back()
here, , saw that:
if reallocation happens, storage allocated using container's allocator, may throw exceptions on failure (for default allocator, bad_alloc thrown if allocation request not succeed).
this seems problem, don't understand why allocation request wouldn't succeed.
for completeness, here files:
token.h
#pragma once #include <string> enum class tokentype { _eof, error, equals, semicolon, plus, less_than, greater_than, int, id, whitespace }; struct token { tokentype type; std::string detail; };
lexer.h
#pragma once #include <string> #include <vector> #include "token.h" class lexer { public: lexer(std::string); ~lexer(void); std::vector<token> lex(void); private: token* getnext(void); void updatecurrentchar(void); void increment(void); bool matchcurrent(char); bool iswhitespace(char) const; bool isdigit(char) const; bool isletter(char) const; bool islowercaseletter(char) const; bool isuppercaseletter(char) const; std::string readwhitespace(void); std::string readint(void); std::string readid(void); std::string input; int inputptr; char currentchar; const char eof_char; };
lexer.cpp
#include "lexer.h" lexer::lexer(std::string _input) : input(_input), eof_char(-1) { } lexer::~lexer(void) { } std::vector<token> lexer::lex(void) { // reset input pointer inputptr = 0; updatecurrentchar(); // read tokens until eof std::vector<token> tokens; token *token = nullptr; { token = getnext(); tokens.push_back(*token); } while(token->type != tokentype::_eof); return tokens; } void lexer::updatecurrentchar(void) { currentchar = inputptr < input.length() ? input[inputptr] : eof_char; } void lexer::increment(void) { inputptr++; updatecurrentchar(); } bool lexer::matchcurrent(char tomatch) { if(tomatch == currentchar) { increment(); return true; } return false; } token* lexer::getnext(void) { token token; if(iswhitespace(currentchar)) { token.type = tokentype::whitespace; token.detail = readwhitespace(); return &token; } if(isdigit(currentchar)) { token.type = tokentype::int; token.detail = readint(); return &token; } if(isletter(currentchar)) { token.type = tokentype::id; token.detail = readid(); return &token; } if(currentchar == eof_char) { token.type = tokentype::_eof; return &token; } switch(currentchar) { case ';': token.type = tokentype::semicolon; case '=': token.type = tokentype::equals; case '<': token.type = tokentype::less_than; case '>': token.type = tokentype::greater_than; case '+': token.type = tokentype::plus; default: token.type = tokentype::error; token.detail = currentchar; } increment(); return &token; } std::string lexer::readwhitespace(void) { std::string ws; while(iswhitespace(currentchar)) { ws += currentchar; increment(); } return ws; } std::string lexer::readint(void) { std::string ws; while(isdigit(currentchar)) { ws += currentchar; increment(); } return ws; } std::string lexer::readid(void) { std::string ws; while(iswhitespace(currentchar)) { ws += currentchar; increment(); } return ws; } bool lexer::isdigit(char c) const { return c >= '0' && c <= '9'; } bool lexer::isletter(char c) const { return islowercaseletter(c) || isuppercaseletter(c); } bool lexer::islowercaseletter(char c) const { return c >= 'a' && c <= 'z'; } bool lexer::isuppercaseletter(char c) const { return c >= 'a' && c <= 'z'; } bool lexer::iswhitespace(char c) const { switch(c) { case ' ': case '\n': case '\t': case '\r': return true; default: return false; } }
the problem in getnext
return pointer local variable. remember when function returns local variables destructed , pointer points destructed object. or, since local variables on stack , stack reused between function calls, can point different. result of dereferencing invalid pointer leads undefined behavior, , it's common undefined behavior cause crashes, may seem work data screwed up.
the obvious solution of course return copy of object, i.e. don't use pointers (which tip in general).
Comments
Post a Comment