Страницы

Поиск по вопросам

среда, 27 ноября 2019 г.

Исключения при работе с итераторами

#c++ #c++11


Решаю следующую задачу: есть некоторый файл, структура которого представляет собой
набор блоков. Каждый блок имеет заголовок и тело. Последний блок отмечен соответствующим
идентификатором. Для обхода блоков в файле пишу собственный итератор:

/**
 * @brief Single-pass input iterator.
 */
class BlockIterator : public std::iterator {
  private:
    friend class FileReader;

    FileReader* reader;

    // Текущий блок, на который указывает итератор.
    std::unique_ptr block;

    BlockIterator(FileReader* r, std::unique_ptr b) noexcept : reader(r),
block(std::move(b)) {
        /*NOP*/
    };

  public:
    BlockIterator& operator++() {
        auto nextBlockOffset = block->offset + block->headerSize + block->bodySize;
        block = reader->getBlockAt(nextBlockOffset);
        //              ^^^ Функция может выкидывать исключение!
        return *this;
    }

    BlockIterator operator++(int) {
        auto retval = *this;
        ++(*this);
        return retval;
    }

    bool operator==(const BlockIterator& other) const {
        return reader == other.reader && block == other.block;
    }

    bool operator!=(const BlockIterator& other) const {
        return !(*this == other);
    }

    reference operator*() const {
        return *block;
    }
};


При инкременте функция getBlockAt() может выкидывать исключение (например, если парсим
невалидный файл). Как в этом случае лучше поступать: необходимо ли гарантировать отсутствие
исключений при работе с итератором? Если да, то как обеспечить инвалидацию итератора
и вывод ошибки?
    


Ответы

Ответ 1



Поскольку есть наследование от std::iterator<...>, то имеет смысл сохранять его идеологию обработки подобных ошибок. Согласно последнему опубликованному черновику стандарта С++, нужно кидать исключение: Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value... Стр. 856, п. 7

Ответ 2



Игнорировать ошибку нельзя, нужно как-то о ней сообщить всегда. Как сообщать зависит от того где вы используете этот итератор, если только в своём коде тогда можно либо метод сделать для проверки ошибочности текущего значения итератора (в этом случае при разыменовании итератора ошибочного нужно обязательно исключение бросать, либо итератор перемещать на end() сразу чтобы обработка завершалась), если вы хотите использовать итератор со стандартной библиотекой типа STL, тогда в идеологии итератора нет возможности вернуть ошибку кроме как через исключение, тогда нужно бросать исключение, но его будет ловить уже не STL код а тот кто им выше пользуется. Если исключения бросать никак нельзя вам по какой-то причине, тогда нужно просто итератор при перемещении на невалидный блок сразу смещать в конец end() либо пытаться пропустить один блок если такое можно сделать и найти как-то следующий блок например по специальному уникальному набору байт в начале заголовка, тогда программа обработки посчитает что обработка полностью завершена, хотя будут не обработанные блоки, это не приведёт к краху программы, просто данные не будут все обработаны, такое поведение часто бывает у обработчиков разных данных, они если не могут сообщить об ошибке тогда пытаются пропускать битые данные и сохранять где-то информацию об ошибках в статистике или логе внутри своих структур, тогда потом можно после работы узнать сколько битых блоков было пропущено и были ли вообще ошибки.

Комментариев нет:

Отправить комментарий