7.3.4 Обработка Ошибок
7.3.4 Обработка Ошибок
Есть четыре подхода к проблеме, что же делать, когда во время выполнения универсальное средство вроде slist сталкивется с ошибкой (в С++ нет никаких специальных средств языка для обработки ошибок):
1. Возвращать недопустимое значение и позволить пользвателю его проверять
2. Возвращать дополнительное значение состояния и рарешить пользователю проверять его
3. Вызывать функцию ошибок, заданную как часть класса slist или
4. Вызывать функцию ошибок, которую предположительно предоставляет пользователь.
Для небольшой программы, написанной ее единственным пользователем, нет фактически никаких особенных причин препочесть одно из этих решений другим. Для средства общего наначения ситуация совершенно иная.
Первый подход, возвращать недопустимое значение, неосществим. Нет совершенно никакого способа узнать, что некоторое конкретное значение будет недопустимым во всех прменениях slist.
Второй подход, возвращать значение состояния, можно ипользовать в некоторых классах (один из вариантов этого плана применяется в стандартных потоках ввода/вывода istream и ostream; как – объясняется в #8.4.2). Здесь, однако, имеется серьезная проблема, вдруг пользователь не позаботится проврить значение состояния, если средство не слишком часто поводит. Кроме того, средство может использоваться в сотнях или даже тысячах мест программы. Проверка значения в каждом месте сильно затруднит чтение программы.
Третьему подходу, предоставлять функцию ошибок, недостет гибкости. Тот, кто реализует универсальное средство, не может узнать, как пользователи захотят, чтобы обрабатывались ошибки. Например, пользователь может предпочитать сообщения на датском или венгерском.
Четвертый подход, позволить пользователю задавать фунцию ошибок, имеет некоторую привлекательность при условии, что разработчик предоставляет класс в виде библиотеки (#4.5), в которой содержатся стандартные функции обработки ошибок.
Решения 3 и 4 можно сделать более гибкими (и по сути эвивалентными), задав указатель на функцию, а не саму функцию. Это позволит разработчику такого средства, как slist, предотавить функцию ошибок, действующую по умолчанию, и при этом программистам, которые будут использовать списки, будет легко задать свои собственные функции ошибок, когда нужно, и там, где нужно. Например:
typedef void (*PFC)(char*); // указатель на тип функция extern PFC slist_handler; extern PFC set_slist_handler(PFC);
Функция set_slist_hanlder() позволяет пользователю замнить стандартную функцию. Общепринятая реализация предосталяет действующую по умолчанию функцию обработки ошибок, котрая сначала пишет сообщение об ошибке в cerr, после чего завершает программу с помощью exit():
#include «slist.h» #include «stream.h»
void default_error(char* s)
(* cerr «„ s «« « “; exit(1); *)
Она описывает также указатель на функцию ошибок и, для удобства записи, функцию для ее установки:
PFC slist_handler = default_error;
PFC set_slist_handler(PFC handler); (* PFC rr = slist_handler; slist_handler = handler; return rr; *)
Обратите внимание, как set_slist_hanlder() возвращает предыдущий slist_hanlder(). Это делает удобным установку и переустановку обработчиков ошибок на манер стека. В основном это может быть полезным в больших программах, в которых slist может использоваться в нескольких разных ситуациях, в каждой из которых могут, таким образом, задаваться свои собственные подпрограммы обработки ошибок. Например:
(* PFC old = set_slist_handler(my_handler);
// код, в котором в случае ошибок в slist // будет использоваться мой обработчик my_handler
set_slist_handler(old); // восстановление *)
Чтобы сделать управление более изящным, slist_hanlder мог бы быть сделан членом класса slist, что позволило бы раличным спискам иметь одновременно разные обработчики.