Правильный способ вернуть istream из функции - PullRequest
1 голос
/ 22 декабря 2019

Я тестирую скорость различных методов чтения данных из istream, поэтому я создал функцию для многократного создания istream из файла, чтобы следующий протестированный метод мог использовать этот istream.

using std::istream;
using std::string;

istream* getStreamFrom(string filepath)
{
    std::filebuf init_buffer;
    if (init_buffer.open(filepath, std::ios::in))
    {
        std::istream inputStream(&init_buffer);
        init_buffer.close();
        return &inputStream;
    }

    // file not found
    istream inputStream(0);
    return &inputStream;
}

И затем я передаюistream к тестируемому методу, подобному следующему:

istream* data = getStreamFrom(FILEPATH);
someMethod(*data);

Но в someMethod я получаю нарушение прав доступа при чтении из istream:

void someMethod(istream& input)
{
    string line;
    while (std::getline(input, line))    // code failes here
        // do something
}

Почему это происходит и как я могу это исправить? Не имеет значения, найден файл или нет, это происходит в обоих случаях.

Ответы [ 2 ]

1 голос
/ 22 декабря 2019

Если вы собираетесь вернуть локальный поток из функции, то вам нужно как динамически выделить поток и его буфер. Как пояснили комментарии и ответ, вы возвращаете указатели на локальные переменные, которые будут уничтожены после выхода из функции. Стандартные потоковые классы std::istream и std::ostream не являются подвижными, поэтому единственный вариант - создать объекты в куче. И поток, и буфер могут быть обернуты в умный указатель, чтобы избежать ручного управления памятью.

std::unique_ptr<std::istream> getStreamFrom(std::string filepath) {
  auto init_buf = std::make_unique<std::filebuf>();
  return std::make_unique<std::istream>(
    init_buf->open(filepath, std::ios_base::in)
      ? init_buf.release()->close()
      : nullptr
  );
}

Кстати, std::istream, инициализированный std::filebuf, равен std::fstream. Вы также можете вернуть указатель на это:

std::unique_ptr<std::ifstream> getStreamFrom(std::string filepath) {
  auto stream = std::make_unique<std::ifstream>(filepath);
  stream->close();
  return std::unique_ptr<std::ifstream>( *stream ? std::move(stream) : nullptr );
}

с необязательным в C ++ 17:

std::optional<std::ifstream> getStreamFrom(std::string filepath) {
  std::ifstream stream(filepath);
  stream.close();
  return stream ? std::optional(std::move(stream)) : std::nullopt;
}
0 голосов
/ 22 декабря 2019

inputStream выйдет из области видимости после выхода из вашей функции. Доступ к std::istream* вне функции приведет к неопределенному поведению.

Вероятно, лучший способ сделать это с std::shared_ptr. Примерно так:

std::shared_ptr<std::istream> getStreamFrom(string filepath)
{
    std::filebuf init_buffer;
    if (init_buffer.open(filepath, std::ios::in))
    {
        std::shared_ptr<std::istream> inputStream = std::make_shared<std::istream>(&init_buffer);
        init_buffer.close();
        return inputStream;
    }

    // file not found
    std::shared_ptr<std::istream> inputStream = std::make_shared<istream>(nullptr);
    return inputStream;
}

Конечно, init_buffer тоже собирается выйти за рамки. Не совсем уверен, что правильно делать, так как передача его непосредственно в std::istream не удалит буфер . Я не знаю, как правильно сделать это, потому что вы не делаете свой собственный std::istream, поэтому предлагаемое решение не сработает для вас. Так что вы вроде как для этого. Но вы были предупреждены.

Одна вещь, которую вы могли бы сделать, это вместо этого std::move() это. Вот немного дополнительной информации о std::move() вещах .

...