9.4 Реализация

Предыдущая  Содержание  Следующая V*D*V

Если мы принимаем решение заменить имя описания класса, такое как 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 не требуется; тем не менее, список классов в программе может пригодиться для некоторой других целей.

 

Предыдущая  Содержание  Следующая