Бьерн Страуструп - Язык программирования С++. Главы 8-10 - Состояния потока

ОГЛАВЛЕНИЕ


10.3.2 Состояния потока

С каждым потоком (istream или ostream) связано определенное состояние. Нестандартные ситуации и ошибки обрабатываются с помощью проверки и установки состояния подходящим образом. Узнать состояние потока можно с помощью операций над классом ios:
     class ios { //ios является базовым для ostream и istream
         //...
     public:
         int eof() const;     // дошли до конца файла
         int fail() const;    // следующая операция будет неудачна
         int bad() const;     // поток испорчен
         int good() const;    // следующая операция будет успешной
         //...
     };
Последняя операция ввода считается успешной, если состояние задается good() или eof(). Если состояние задается good(), то последующая операция ввода может быть успешной, в противном случае она будет неудачной. Применение операции ввода к потоку в состоянии, задаваемом не good(), считается пустой операцией. Если произошла неудача при попытке чтения в переменную v, то значение v не изменилось (оно не изменится, если v имеет тип, управляемый функциями члена из istream или ostream). Различие между состояниями, задаваемыми как fail() или как bad() уловить трудно, и оно имеет смысл только для разработчиков операций ввода. Если состояние есть fail(), то считается, что поток не поврежден, и никакие символы не пропали; о состоянии bad() ничего сказать нельзя.

Значения, обозначающие эти состояния, определены в классе ios:

     class ios {
         //...
     public:
         enum io_state {
             goodbit=0,
             eofbit=1,
             filebit=2,
             badbit=4,
         };
         //...
     };
Истинные значения состояний зависят от реализации, и указанные значения приведены только, чтобы избежать синтаксически неправильных конструкций. Проверять состояние потока можно следующим образом:
     switch (cin.rdstate()) {
     case ios::goodbit:
         // последняя операция с cin была успешной
         break;
     case ios::eofbit:
         // в конце файла
         break;
      case ios::filebit:
         // некоторый анализ ошибки
         // возможно неплохой
         break;
       case ios::badbit:
         //  cin возможно испорчен
         break;
       }
В более ранних реализациях для значений состояний использовались глобальные имена. Это приводило к нежелательному засорению пространства именования, поэтому новые имена доступны только в пределах класса ios. Если вам необходимо использовать старые имена в сочетании с новой библиотекой, можно воспользоваться следующими определениями:
     const int _good = ios::goodbit;
     const int _bad = ios::badbit;
     const int _file = ios::filebit;
     const int _eof = ios::eofbit;

     typedef ios::io_state state_value ;
Разработчики библиотек должны заботится о том, чтобы не добавлять новых имен к глобальному пространству именования. Если элементы перечисления входят в общий интерфейс библиотеки, они всегда должны использоваться в классе с префиксами, например, как ios::goodbit и ios::io_state.

Для переменной любого типа, для которого определены операции < и >>, цикл копирования записывается следующим образом:

      while (cin>>z) cout << z << '\n';
Если поток появляется в условии, то проверяется состояние потока, и условие выполняется (т.е. результат его не 0) только для состояния good(). Как раз в приведенном выше цикле проверяется состояние потока
istream, что является результатом операции cin>>z. Чтобы узнать, почему произошла неудача в цикле или условии, надо проверить состояние. Такая проверка для потока реализуется с помощью операции приведения (7.3.2).

Так, если z является символьным вектором, то в приведенном цикле читается стандартный ввод и выдается для каждой строки стандартного вывода по одному слову (т.е. последовательности символов, не являющихся обобщенными пробелами). Если z имеет тип complex, то в этом цикле с помощью операций, определенных в 10.2.2 и 10.2.3, будут копироваться комплексные числа. Шаблонную функцию копирования для потоков со значениями произвольного типа можно написать следующим образом:

     complex z;
     iocopy(z,cin,cout);    // копирование complex

     double d;
     iocopy(d,cin,cout);    // копирование double
     char c;
     iocopy(c,cin,cout);    // копирование char
Поскольку надоедает проверять на корректность каждую операцию ввода-вывода, то распространенным источником ошибок являются именно те места в программе, где такой контроль существенен. Обычно операции вывода не проверяют, но иногда они могут завершиться неудачно. Потоковый ввод-вывод разрабатывался из того принципа, чтобы сделать исключительные ситуации легкодоступными, и тем самым упростить обработку ошибок в процессе ввода-вывода.