The website "dmilvdv.narod.ru." is not registered with uCoz.
If you are absolutely sure your website must be here,
please contact our Support Team.
If you were searching for something on the Internet and ended up here, try again:

About uCoz web-service

Community

Legal information

9.4 Реализация

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

 

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