Почему это сегфолтинг? - PullRequest
0 голосов
/ 10 февраля 2020

У меня есть следующий конструктор, который строит Tokens:

// token_builder.h
class TokenBuilder
{
    Keyword m_keyword;
    TokenType m_type;
    std::string m_symbol;

public:
    TokenBuilder& set_keyword(Keyword);
    TokenBuilder& set_toktype(TokenType);
    TokenBuilder& set_symbol(std::string);
    std::unique_ptr<Token>&& build();
};
// token_builder.cpp
TokenBuilder& TokenBuilder::set_keyword(Keyword k)
  {
      m_keyword = k;
      return *this;
  }

  TokenBuilder& TokenBuilder::set_toktype(TokenType t)
  {
      m_type = t;
      return *this;
  }

  TokenBuilder& TokenBuilder::set_symbol(std::string sym)
  {
      m_symbol = sym; // This is the point where it segfaults
      return *this;
  }

  std::unique_ptr<Token>&& TokenBuilder::build()
  {
      std::unique_ptr<Token> token;
      token->m_keyword = m_keyword;
      token->m_type = m_type;
      token->m_symbol = m_symbol;
      return std::move(token);
  }

Программа останавливается с SIGSEGV в точке, где я пытаюсь инициализировать m_symbol, вызывая TokenBuilder::set_symbol , Вот как выглядит main:

int main()
{
    auto token = Token::builder()
            .set_keyword(Keyword::IF)
            .set_toktype(TokenType::ID)
            .set_symbol("if")
            .build();

    std::cout << token->get_symbol() << std::endl;
}

Как видите, вызов set_keyword и set_toktype не приводит к ошибкам. Но передача литерала «if» (или объекта std::string) в set_symbol заставляет программу обрабатывать sh. Передача по значению является преднамеренной. Вступление в set_symbol и анализ состояния TokenBuilder ничего не показывает. Как и ожидалось, m_keyword установлен на Keyword::IF и m_type на TokenType::ID. Это строка, которая дает сбой: m_symbol = sym.

В программе, приведенной выше, могут быть другие нетривиальные проблемы (правильно ли я использую std::move, вернувшись в C ++ через долгое время), но такое чувство как глупая ошибка, которую совершил бы ладья ie. Я использую MinGW-w64 компилятор на Windows 10 (64-бит).

Вот весь код для удобного копирования и вставки на ваш компьютер:

// src/include/token_builder.h
#ifndef TOKEN_BUILDER_H
#define TOKEN_BUILDER_H

#include <memory>
#include "token.h"

namespace funk
{
class Token;

class TokenBuilder
{
    Keyword m_keyword;
    TokenType m_type;
    std::string m_symbol;

public:
    TokenBuilder& set_keyword(Keyword);
    TokenBuilder& set_toktype(TokenType);
    TokenBuilder& set_symbol(std::string);
    std::unique_ptr<Token>&& build();
};

}

#endif
// src/token_builder.cpp
#include "include/token.h"
#include "include/token_builder.h"

namespace funk
{
  TokenBuilder& TokenBuilder::set_keyword(Keyword k)
  {
      m_keyword = k;
      return *this;
  }

  TokenBuilder& TokenBuilder::set_toktype(TokenType t)
  {
      m_type = t;
      return *this;
  }

  TokenBuilder& TokenBuilder::set_symbol(std::string sym)
  {
      m_symbol = sym;
      return *this;
  }

  std::unique_ptr<Token>&& TokenBuilder::build()
  {
      std::unique_ptr<Token> token;

      token->m_is_keyword = m_keyword != Keyword::_NONE_;
      token->m_keyword = m_keyword;
      token->m_type = m_type;
      token->m_symbol = m_symbol;

      return std::move(token);
  }
}
// src/include/token.h
#ifndef TOKEN_H
#define TOKEN_H
#include <memory>
#include "keyword.h"
#include "token_type.h"
#include "token_builder.h"

namespace funk
{
  class TokenBuilder;

  class Token
  {
      friend class TokenBuilder;

      Keyword m_keyword;
      bool m_is_keyword;
      std::string m_symbol;
      TokenType m_type;

      Token();

  public:

      Keyword& get_keyword ();
      bool is_keyword();
      std::string& get_symbol();
      TokenType& get_type();
      static TokenBuilder&& builder();
  };
}

#endif // END TOKEN_H
// src/token.cpp
#include <string>
#include <memory>
#include "include/keyword.h"
#include "include/token_builder.h"
#include "include/token_type.h"

namespace funk
{
  Keyword& Token::get_keyword()
  {
    return m_keyword;
  }

  bool Token::is_keyword()
  {
    return m_is_keyword;
  }

  std::string& Token::get_symbol()
  {
    return m_symbol;
  }

  TokenType& Token::get_type()
  {
    return m_type;
  }

  TokenBuilder&& Token::builder()
  {
    TokenBuilder builder;
    return std::move(builder);
  }
}

// src/main.cpp
#include <iostream>
#include "include/buffered_reader.h"
#include "include/token.h"
#include "include/token_builder.h"

int main()
{
    using namespace funk;

    auto token = Token::builder()
            .set_keyword(Keyword::IF)
            .set_toktype(TokenType::ID)
            .set_symbol("if")
            .build();

    std::cout << token->get_symbol() << std::endl;
}

Примечание: Я искал похожие вопросы в StackOverflow как можно лучше, но не нашел вопроса, соответствующего моему случаю. Если вы думаете, что это похоже на вопрос, с которым вы столкнулись ранее, пометьте дубликат и опубликуйте ссылку на оригинальный вопрос. Спасибо!

1 Ответ

2 голосов
/ 10 февраля 2020
  std::unique_ptr<Token>&& TokenBuilder::build()
  {
      std::unique_ptr<Token> token;
      // ...
      return std::move(token);
  }

Вы возвращаете ссылку на локальную переменную. В конце функции локальная переменная уничтожается, и возвращаемая ссылка останется со ссылкой на объект за пределами его времени жизни. Когда вы позже попытаетесь получить доступ к несуществующему объекту через свисающую ссылку, поведение программы не определено.

...