📚 Hub Books: Онлайн-чтение книгРазная литератураC++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+
1 ... 74 75 76 77 78 79 80 81 82 ... 121
Перейти на страницу:
с плавающей точкой, в верхнем регистре и явным префиксом +, если они имеют положительные значения. Кроме того, мы используем наш класс format_guard, чтобы очистить все настройки при выходе из функции:

template <typename T>

ostream& operator<<(ostream &os, const scientific_type<T> &w) {

  format_guard _;

  os << scientific << uppercase << showpos;

  return os << w.value;

}

5. В функции main сначала поработаем с классом format_guard. Откроем новую область видимости, получим экземпляр класса, а затем применим некоторые флаги форматирования к потоку вывода std::cout:

int main()

{

  {

    format_guard _;

    cout << hex << scientific << showbase << uppercase;

    cout << "Numbers with special formatting:n";

    cout << 0x123abc << 'n';

    cout << 0.123456789 << 'n';

  }

6. После того как выведем некоторые числа с помощью флагов форматирования, покинем область видимости. В результате деструктор класса format_guard сбросит настройки форматирования. Чтобы это протестировать, выведем точно такие же числа снова. Они должны выглядеть по-другому:

  cout << "Same numbers, but normal formatting again:n";

  cout << 0x123abc << 'n';

  cout << 0.123456789 << 'n';

7. Теперь воспользуемся классом scientific_type. Выведем на экран три числа с плавающей точкой в ряд. Второе число обернем в класс scientific_type.

Соответственно, оно будет выведено с применением нашего особенного стиля форматирования, а числа, стоящие перед ним и после него, будут иметь форматирование по умолчанию. В то же время мы избежим появления ненужных символов.

  cout << "Mixed formatting: "

       << 123.0 << " "

       << scientific_type{123.0} << " "

       << 123.456 << 'n';

}

8. Компиляция и запуск программы дадут следующий результат. Первые два числа будут выведены с конкретным форматированием. Следующие два будут иметь форматирование по умолчанию — это показывает, что наш класс format_guard работает хорошо. Три числа в последних строках также выглядят соответственно нашим ожиданиям. Число, которое стоит посередине, имеет форматирование класса scientific_type, остальные же имеют форматирование по умолчанию:

$ ./pretty_print_on_the_fly

Numbers with special formatting:

0X123ABC

1.234568E-01

Same numbers, but normal formatting again:

1194684

0.123457

Mixed formatting: 123 +1.230000E+02 123.456 

Перехватываем читабельные исключения для ошибок потока std::iostream

Ни в одном из примеров, показанных в данной главе, мы не использовали для обнаружения ошибок исключения. Несмотря на такую возможность, можно работать с объектами потоков без исключений — это очень удобно. Если мы попробуем преобразовать десять значений, но где-то в середине будет сгенерирована ошибка, то весь объект потока войдет в ошибочное состояние и перестанет работать. Таким образом, мы не столкнемся с ситуацией, когда преобразуем переменные с ошибочным смещением в потоке. Мы можем выполнять условные преобразования, например, так: if (cin >> foo >> bar >> ...). В случае сбоя этой конструкции мы его обработаем. Использование конструкции try {...} catch... при преобразовании объектов не кажется очень полезным.

Фактически библиотека для работы с потоками ввода/вывода в C++ существовала уже в те времена, когда язык C++ еще не работал с исключениями. Поддержка исключений была добавлена позже, это объясняет отсутствие их первоклассной поддержки в потоковой библиотеке.

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

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

Как это делается

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

1. Сначала включим некоторые заголовочные файлы и объявим об использовании пространства имен std:

#include <iostream>

#include <fstream>

#include <system_error>

#include <cstring>

using namespace std;

2. Для использования объектов потока вместе с исключениями нужно их активизировать. Чтобы объект файлового потока генерировал исключение в том случае, если нужный файл не существует или при преобразовании возникли ошибки, следует установить значения некоторых битов, указывающих на сбой, в маске исключения. Если мы затем сделаем нечто вызывающее сбой, это сгенерирует исключение. Активизируя failbit и badbit, мы включаем генерацию исключений для ошибок файловой системы и преобразования:

int main()

{

  ifstream f;

  f.exceptions(f.failbit | f.badbit);

3. Теперь можно открыть блок try и обратиться к файлу. Если последний был открыт успешно, то попробуем считать из него целое число. При успешном выполнении обоих шагов выведем целое число:

  try {

    f.open("non_existant.txt");

    int i;

    f >> i;

    cout << "integer has value: " << i << 'n';

  }

4. Если хотя бы в одной из этих ситуаций возникнет ошибка, то будет сгенерирован экземпляр std::ios_base::failure. Данный объект имеет функцию-член what(), которая должна объяснить, что вызвало генерацию исключения. К сожалению, это сообщение не стандартизировано и не слишком информативно. Однако мы можем хотя бы определить, это проблема с файловой системой (например, файл не существует) или же ошибка преобразования. Глобальная переменная errno существовала с момента создания C++, и она получает значение ошибки, которое мы сейчас можем получить. Функция strerror преобразует номер ошибки в строку, понятную для человека. Если код равен 0, то значит, у нас возникла не ошибка файловой системы.

  catch (ios_base::failure& e) {

    cerr << "Caught error: ";

    if (errno) {

      cerr << strerror(errno) << 'n';

    } else {

      cerr << e.what() << 'n';

1 ... 74 75 76 77 78 79 80 81 82 ... 121
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?