Вход/Регистрация
C++
вернуться

Страустрап Бьярн

Шрифт:

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

Четвертый подход, позволить пользователю задавать фунцию ошибок, имеет некоторую привлекательность при условии, что разработчик предоставляет класс в виде библиотеки (#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 «« «\n“; 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, что позволило бы раличным спискам иметь одновременно разные обработчики.

7.3.5 Обобщенные классы

Очевидно, можно было бы определить списки других типов (classdef*, int, char* и т.д.) точно так же, как был опредлен класс nlist: простым выводом из класса slist. Процесс оределения таких новых типов утомителен (и потому чреват ошиками), но с помощью макросов его можно «механизировать». К сожалению, если пользоваться стандартным C препроцессором (#4.7 и #с.11.1), это тоже может оказаться тягостным. Однако полученными в результате макросами пользоваться довольно просто.

Вот пример того, как обобщенный (generic) класс slist, названный gslist, может быть задан как макрос. Сначала для написания такого рода макросов включаются некоторые инстрменты из «generic.h»:

#include «slist.h»

#ifndef GENERICH #include «generic.h» #endif

Обратите внимание на использование #ifndef для того, чтобы гарантировать, что «generic.h» в одной компиляции не будет включен дважды. GENERICH определен в «generic.h».

После этого с помощью name2, макроса из «generic.h» для конкатенации имен, определяются имена новых обобщенных

классов:

#define gslist(type) name2(type,gslist) #define gslist_iterator(type) name2(type,gslist_iterator)

И, наконец, можно написать классы gslist(тип) и gslist_iterator(тип):

#define gslistdeclare(type) \ struct gslist(type) : slist (* \ int insert(type a) \ (* return slist::insert( ent(a) ); *) \ int append(type a) \ (* return slist::append( ent(a) ); *) \ type get (* return type( slist::get ); *) \ gslist(type) (* *) \ gslist(type)(type a) : (ent(a)) (* *) \ ~gslist(type) (* clear; *) \ *); \ \ struct gslist_iterator(type) : slist_iterator (* \ gslist_iterator(type)(gslist(type) amp; a) \ : ( (slist amp;)s ) (**) \ type operator \ (* return type( slist_iterator::operator ); *)\ *)

\ на конце строк указывает , что следующая строка явлется частью определяемого макроса.

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

#include «name.h»

typedef name* Pname; declare(gslist,Pname); // описывает класс gslist(Pname)

gslist(Pname) nl; // описывает один gslist(Pname)

Макрос declare (описать) определен в «generic.h». Он конкатинирует свои параметры и вызывает макрос с этим именем, в данном случае gslistdeclare, описанный выше. Параметр имя типа для declare должен быть простым именем. Используемый мтод макроопределения не может обрабатывать имена типов вроде name*, поэтому применяется typedef.

  • Читать дальше
  • 1
  • ...
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: