9.4 Реализация |
Предыдущая Содержание Следующая |
Если мы принимаем решение заменить имя описания класса, такое как Point, вызовом функции инициализации Point(), чтобы сгенерировать описание класса автоматически, то должны изменить каждое использование описания класса, и должны подправить отчёты ooc для генерации немного других файлов. Имена описаний классов используются в вызовах new(), cast(), isA(), isOf() и в вызовах селектора суперкласса. Используя функции вместо переменных указателя представляет собой новое соглашение, то есть мы должны будем изменить прикладные программы и файлы реализации. Хороший компилятор ANSI-C (или компилятор GNU-C с опцией -pedantic) может быть довольно полезным: надо отметить все попытки передать имя функции параметру void *, то есть он должен отметить все те точки в нашем коде C, где мы пропустили добавление пустого списка аргументов к имени класса. Изменение отчётов немного более трудная работа. Помогает просмотр сгенерированных файлов для поиска ссылок на описания классов. Файл представления Point.r остаётся неизменным. Интерфейсный файл Point.h декларирует указатели описания класса и метакласса. Это изменится
extern const void * Point; extern const void * PointClass;
на это
extern const void * const Point (void); extern const void * const PointClass (void);
где выделенный жирным const может использоваться только с GNU-C. Это помогает иметь переносимый отчёт, таким образом, мы изменяем соответствующие строки в h.rep следующим образом
extern const void * `%const `class (void); `n `n extern const void * `%const `meta (void); `n `n
и добавляем новый отчёт в общий файл header.rep:
% const // GNUC поддерживает функции с const
`{if `GNUC 1 const `}
ooc обычно определяет символ GNUC с нулевым значением, но указав
$ ooc -DGNUC=1 ...
мы можем установить этот символ в 1 в командной строке и сгенерировать лучший код. Файл реализации Point.c содержит много изменений. Изменяются все cast(); по большей части они создаются в ooc запросом %casts и, таким образом, отчётами casts и checks, показанными в разделе 8.4. Другие вызовы cast() используются в некоторых селекторах и селекторах суперклассов и в конструкторах метаклассов, но они генерируются отчётами в etc.rep, c.rep и c-R.rep. Теперь окупается использование ooc для претворения в жизнь нашего стандарта кодирования — этот стандарт легко изменить в единственном месте. Таким существенным изменением является конечно же новый стиль функций инициализации. К счастью, они также генерируются в c.rep и мы получаем новые версии, преобразуя Point() как показано в предыдущем параграфе для формата отчёта в c.rep. Наконец, создаём такие функции по умолчанию, как
const void * const Object (void) { return & _Object; }
отчётом init в c-R.rep, чтобы можно было извлечь выгоду из условного выражения GNUC для ooc. Это немного рисковано, потому что как указано в разделе 7.5 статическая инициализация _Object должна быть закодирована в Object.dc:
extern const struct Class _Object; extern const struct Class _Class;
%init
static const struct Class _Object = { { MAGIC, & _Class }, "Object", & _Object, sizeof(struct Object), Object_ctor, Object_dtor, Object_differ, Object_puto };
extern вводит предваряющие ссылки на описания. %init генерирует функции, которые ссылаются на описания, как показано выше. static, наконец, задаёт внутреннюю связь с инициализированными описаниями, то есть они всё ещё скрыты в файле реализации Object.c. Как исключение, _Object должен быть названием самой структуры, а не указателя на неё, чтобы & _Object можно было использовать для инициализации этой структуры. Если мы не введём такой макрос, как Class(), это будет иметь мало значения, но немного усложнит munch:
NF != 3 || $1 !˜ /ˆ[0-9a-f]+$/ { next } $2 ˜ /ˆ[bs]$/ { bsd[$3] = 1; next } $2 == "d" { sysv[$3] = 1; next } $2 == "T" { T[$3] = 1; next }
END { for (name in T) if ("_" name in bsd) # eliminate leading _ names[n ++] = substr(name, 2) else if ("_" name in sysv) names[n ++] = name
for (i = 0; i < n; ++ i) printf "extern const void * %s (void);\n", names[i]
print "\nconst void * (* classes [])(void) = {" for (i = 0; i < n; ++ i) printf "\t%s,\n", names[i] print "0 };" }
Название класса должно теперь встречаться как глобальная функция и с начальным символом подчеркивания, как и у локального элемента данных. Флаги -nm в Berkeley инициализируют локальные данные с s и не инициализируют данные с b, -nm в System-V в обоих случаях использует d. Мы просто собираем все интересующие символы в трёх массивах и сопоставляем их в END, чтобы создать массив names[], в котором мы на самом деле нуждаемся. Такая архитектура даже имеет преимущество: мы можем вставить простую сортировку методом Шелла [K&R88] для получения названий классов в алфавитном порядке:
for (gap = int(n/2); gap > 0; gap = int(gap/2)) for (i = gap; i < n; ++ i) for (j = i-gap; j >= 0 && \ names[j] > names[j+gap]; j -= gap) { name = names[j] names[j] = names[j+gap] names[j+gap] = name }
Если мы используем вызовы функций вместо названий классов, munch не требуется; тем не менее, список классов в программе может пригодиться для некоторой других целей.
|
Предыдущая Содержание Следующая |