11.3 Реализация методов классов

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

Внутреннее различие между методом класса и другим динамически компонуемым методом скрывается в селекторе. Рассмотрим динамически компонуемый метод exec() для расчёта узла. Для получения описания класса селектор применяет classOf() и ищет .exec в этом месте:

 

double exec (const void * _self) {

    const struct NodeClass * class =

                        cast(NodeClass(), classOf(_self));

 

    assert(class -> exec.method);

    return ((double (*) ()) class -> exec.method)(_self);

}

 

В противоположность ему рассмотрим new(), метод класса, который применяется к описанию класса. В этом случае self ссылается на само описание класса и селектор ищет .new как компонент *self:

 

struct Object * new (const void * _self, ...) {

    struct Object * result;

    va_list ap;

    const struct Class * self = cast(Class(), _self);

 

    assert(self -> new.method);

    va_start(ap, _self);

    result = ((struct Object * (*) ()) self -> new.method)

                                           (_self, & ap);

    va_end(ap);

    return result;

}

 

Вот картинка, описывающая связь exec() и new():

 

OOC_Implementing_Class_Methods

 

И методы классов, и динамически компонуемые методы, используют один и тот же селектор суперкласса, потому что он получает указатель описания класса в виде явного параметра.

 

struct Object * super_new (const void * _class,

                        const void * _self, va_list * app) {

    const struct Class * superclass = super(_class);

 

    assert(superclass -> new.method);

    return

        ((struct Object * (*) ()) superclass -> new.method)

                                               (_self, app);

}

 

Селекторы генерируются ooc под управлением отчёта selectors в etc.rep. Поскольку селекторы отличаются для методов класса и динамически компонуемых методов, ooc должен знать способ компоновки метода. Поэтому методы классов определены в файле описания класса после динамически компонуемых методов и разделителя %+. Вот куски кода из Object.d:

 

% Class Object {

        ...

%

    const Class @ classOf (const _self); // класс объекта

        ...

%-

    void * ctor (_self, va_list * app);  // конструктор

        ...

    void delete (_self);                 // возвращение экземпляра

%+

    Object @ new (const _self, ...);     // создание экземпляра

%}

 

delete() перемещён к динамически компонуемым методам, а new() представлен как метод класса.

 

% Class Class: Object {

    ...

%

    Object @ allocate (const _self);     // память для экземпляра

    ...

%}

 

После удаления new() из списка статически компонуемых методов для Class, мы упаковываем группу распределения памяти новым статически компонуемым методом allocate().

ooc узнаёт компоновку каждого метода с помощью разделителей %- и %+, а отчёт selectors может быть расширен для генерации селекторов, показанных выше. Другие отчёты генерируют декларации селекторов для файла интерфейса, декларации селекторов суперклассов и размещение описания метакласса для файла представления, цикл в конструкторе метакласса, который распознаёт тройной набор селектор / тэг / метод и вводит их в описание класса, и, наконец, функции инициализации для описаний классов и метаклассов. Все эти отчёты должны быть расширены. Например, в отчёте -h в h.rep декларации динамически компонуемых методов генерируются с помощью

 

`{%— `%header ; `n `}n

 

Новый цикл добавляет декларации методов классов:

 

`{%+ `%header ; `n `}n

 

`{+ представляет собой цикл по методам классов текущего класса.

Так как мы обращаемся к new() и delete() через селекторы, то должны реализовать их для Object в Object.dc:

 

% Object new {

%casts

    return ctor(allocate(self), app);

}

 

new() создаёт область для нового объекта и вызывает подходящий конструктор для его инициализации. allocate() в основном содержит старый код new(). Он получает динамическую память и вставляет указатель описания класса, так что динамическая компоновка ctor() в new() работает корректно:

 

% allocate {

    struct Object * object;

%casts

    assert(self -> size);

    object = calloc(1, self -> size);

    assert(object);

    object -> magic = MAGIC;

    object -> class = self;

    return object;

}

 

delete() вызывает деструктор dtor() как и раньше и передаёт результат в free():

 

% Object delete {

%casts

    free(dtor(self));

}

 

Всякий раз, когда мы добавляем методы в Object, обращение к которым происходит через селекторы, мы не должны забыть вставить их вручную в описания классов в Object.dc. Вот пример для _Object:

 

static const struct Class _Object = {

    { MAGIC, & _Class },

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

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

    ...

    { "delete", (Method) delete,(Method) Object_delete },

    ...

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

};

 

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