10.6 Реализация

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

respondsTo() должен искать описание класса по тэгу и вернуть соответствующий селектор. К настоящему времени описание класса содержит только указатели на методы. Очевидно, запись метода в описании класса должна быть расширена:

 

typedef void (* Method) (); // для respondsTo()

 

%prot

 

struct Method {

    const char * tag;       // для respondsTo()

    Method selector;        // возвращается через respondsTo()

    Method method;          // доступен селектору

};

 

% Class Object {

       ...

    Method respondsTo (const _self, const char * tag);

 

Method представляет собой простой функциональный тип, определённый в файле интерфейса для Object. Каждый метод зарегистрирован в описании класса как компонент типа struct Method, который содержит указатели на тэг, селектор и фактический метод. respondsTo() возвращает Method. Компиляторы ANSI-C будут ворчать о неявных приведениях от и к этому типу.

УС учётом данного проекта требуется ещё несколько изменений. В Object.dc надо изменить статическую инициализацию описаний классов Object и Class для использования struct Method:

 

static const struct Class _Object = {

    { MAGIC,    & _Class },

    "Object",    & _Object,     sizeof(struct Object),

    { "",       (Method) 0,     (Method) Object_ctor },

    { "",       (Method) 0,     (Method) Object_dtor },

    { "differ", (Method) differ,(Method) Object_differ },

    ...

};

 

Для создания записи в описании класса для файла представления класса отчёт -r в r.rep использует отчёт link в va.rep. Новая версия отчёта link очень проста:

 

% link         // компонент структуры метакласса

 

struct Method `method ;

 

Наконец, отчёт init в c.rep и c-R.rep использует meta-ctor-loop в etc.rep, чтобы сгенерировать цикл, который динамически заполняет описание класса. Здесь мы также должны работать с новыми типами:

 

% meta-ctor-loop  // selector/tag/method наборы переменных для `class

 

`t while ((selector = va_arg(ap, Method))) `n

`t  { `t const char * tag = va_arg(ap, ` \

                           const char *); `n

   `t `t Method method = va_arg(ap, Method); `n `n

      `{%- `%link-it `}

      `t } `n

 

% link—it         // проверяем и вставляем одну пару селектор/метод

 

`t `t if (selector == (Method) `method ) `n

`t `t { `t if (tag) `n

`t `t `t `t self -> `method .tag = tag, `n

`t `t `t `t self -> `method .selector = selector; `n

`t `t `t self -> `method .method = method; `n

`t `t `t continue; `n

`t `t } `n

 

В качестве аргументов конструктору метакласса вместо пар селектор / метод теперь определяем наборы переменных селектор / тэг / метод. Это должно быть встроено в отчёт init в c.rep. Вот функция инициализации для Wc, сгенерированная ooc:

 

static const void * _Wc;

 

const void * Wc (void) {

    return _Wc ? _Wc :

            (_Wc = new(WcClass(),

                    "Wc", Object(), sizeof(struct Wc),

                    wc, "line", Wc_wc,

                    printFile, "wrap", Wc_printFile,

                    printTotal, "quit", Wc_printTotal,

                    (void *) 0));

}

 

Учитывая наборы переменных селектора / тэга / метода в описании класса respondsTo() написать легко. Благодаря иерархии классов мы можем посчитать, сколько методов содержит описание класса, и можем реализовать respondsTo() полностью в классе Object, даже несмотря на работу с произвольными классами:

 

% respondsTo {

    if (tag && * tag) {

        const struct Class * class = classOf(_self);

        const struct Method * p = & class -> ctor; // first

        int nmeth =

            (sizeOf(class) - offsetof(struct Class, ctor))

            / sizeof(struct Method);    // количество методов Method

        do

            if (p -> tag && strcmp(p -> tag, tag) == 0)

                return p -> method ? p -> selector : 0;

        while (++ p, -- nmeth);

    }

    return 0;

}

 

Единственный недостаток состоит в том, что respondsTo() содержит название самого первого метода в явном виде, ctor, чтобы вычислить количество методов из размера описания класса. Хотя ooc мог бы получить это имя из описания класса Object, было бы довольно неприятно конструировать отчёт для ooc, чтобы генерировать respondsTo() обычным способом.

 

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