Безопасная перегрузка оператора потока››

Существует масса информации о перегрузке operator<< для имитации метода в стиле toString(), который преобразует сложный объект в строку. Меня интересует также реализация обратного operator>> для десериализации строки в объект.

Изучив источник STL, я понял, что:

istream &operator>>(istream &, Object &);

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

  1. Как указать неверные данные в потоке? Выбросить исключение?
  2. В каком состоянии должен находиться поток, если в потоке есть искаженные данные?
  3. Должны ли быть сброшены какие-либо флаги перед возвратом ссылки для цепочки операторов?

person Michael Koval    schedule 15.08.2009    source источник


Ответы (3)


  1. Как указать неверные данные в потоке? Выбросить исключение?

Вы должны установить бит fail. Если пользователь потока хочет, чтобы было выброшено исключение, он может настроить поток (используя istream::exceptions), и поток выдаст соответствующее исключение. Я бы сделал это так, тогда

stream.setstate(ios_base::failbit);
  1. В каком состоянии должен быть поток, если в потоке есть искаженные данные?

Для искаженных данных, которые не соответствуют формату, который вы хотите прочитать, вы обычно должны установить бит fail. Для ошибок внутреннего потока используется бит bad (например, если к потоку не подключен буфер).

  1. Должны ли быть сброшены какие-либо флаги перед возвратом ссылки для цепочки операторов?

Я не слышал о таком.


Для проверки того, находится ли поток в хорошем состоянии, вы можете использовать класс istream::sentry. Создайте из него объект, передав поток и true (чтобы указать ему не пропускать пробелы сразу). Sentry оценивается как false, если установлен бит eof, fail или bad.

istream::sentry s(stream, true);
if(!s) return stream;
// now, go on extracting data...
person Johannes Schaub - litb    schedule 15.08.2009
comment
Также убедитесь, что вы проверили бит fail, прежде чем пытаться что-либо сделать. Если он уже установлен, просто верните поток. - person KTC; 15.08.2009
comment
Спасибо за совет, особенно за использование бита fail вместо исключений. В дополнение к установке бита отказа, должен ли я давать какие-либо гарантии относительно содержимого потока? Например, должен ли поток оставаться неизменным, если я установил бит fail? - person Michael Koval; 15.08.2009
comment
Вот по сути то, что я хотел сказать, но вы ответили быстрее! Я бы добавил, что правильный ответ можно найти, посмотрев, что делают существующие реализации, что вы и описываете. Кроме того, я хотел бы отметить, что не существует такой вещи, как искаженные данные, а неправильный формат для их чтения; в этом случае вы хотите убедиться, что переменная не изменилась, и (если вы установили бит ошибки, а не плохой бит), что ни один символ не был потерян из потока. - person Brooks Moses; 15.08.2009
comment
Не использовать какие-либо символы из потока для плохо отформатированного ввода практически невозможно. Вам понадобится произвольный возврат. Обычное поведение заключается в использовании допустимого префикса. - person AProgrammer; 15.08.2009
comment
Как бы вы порекомендовали обрабатывать ситуации, когда префикс действителен, а фактические данные — нет? - person Michael Koval; 16.08.2009
comment
В операторе››, как это сделано для простых типов (сначала, хотя с плавающей запятой в данном случае только один): вернуть ошибку и потреблять валидный префикс. В пользовательском коде вы можете предоставить лучшую диагностику своему пользователю, прочитав полную строку, а затем проанализировав ее из строкового потока. Но бывают случаи, когда нужно использовать полноценный лексер/парсер, предназначенный для отчетов об ошибках. - person AProgrammer; 16.08.2009
comment
Я бы просто использовал действительный префикс. И держите его в этом. Если вам нужен предварительный синтаксический анализ без фактического потребления чего-либо, тогда я больше не буду использовать iostreams. Они слишком негибки для этого. Я бы использовал потоки C или низкоуровневый интерфейс read iostreams и создал свой собственный буфер упреждающего чтения. - person Johannes Schaub - litb; 16.08.2009

Некоторые дополнительные примечания:

  • при реализации оператора>> вам, вероятно, следует рассмотреть возможность использования bufstream, а не других перегрузок оператора>>;

  • исключения, возникающие во время операции, должны быть преобразованы в фейлбит или бэдбит (члены streambuf могут генерировать ошибки, в зависимости от используемого класса);

  • установка состояния может бросить; если вы устанавливаете состояние после перехвата исключения, вы должны распространять исходное исключение, а не то, которое сгенерировано setstate;

  • ширина — это поле, на которое следует обратить внимание. Если вы принимаете это во внимание, вы должны сбросить его на 0. Если вы используете другой оператор >> для выполнения основных работ, вам нужно вычислить ширину, которую вы передаете, из той, которую вы получили;

  • подумайте о том, чтобы принять во внимание локаль.

Ланге и Крефт (Стандартные потоки ввода-вывода и локали C++) преобразовывают это еще более подробно. Они дают код шаблона для обработки ошибок, который занимает около одной страницы.

person AProgrammer    schedule 15.08.2009
comment
Почему вы предлагаете использовать bustream вместо других перегрузок (например, istream? Я обязательно посмотрю работу Ланге и Крефта - это звучит именно так, как я ищу. - person Michael Koval; 16.08.2009
comment
Привет, я вижу, что и Майкл, и AProgrammer не так активны, поэтому, если кто-то еще увидит это: Почему bustream лучше, чем перегрузка? - person Tomer; 29.04.2018