14.2 Реализация

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

К счастью, в главе 7 мы решили осуществить наш стандарт кодирования с помощью препроцессора ooc, а селекторы — часть стандарта кодирования. В разделе 8.4 мы рассмотрели отчёт selector, который генерирует все селекторы. Как показано выше, передача сообщений выполняется декларацией forward(), определением реализации по умолчанию и модификацией отчёта selector в etc.rep таким образом, чтобы все сгенерированные селекторы перенаправляли всё, что они не понимают: (* Как и ранее, представление упрощено, чтобы не показывать части, которые имеют дело со списками с переменным количеством аргументов.)

 

`%header { `n

`%result

`%classOf

 

`%ifmethod

`%checks

`%call

`t } else `n

`%forward

`%return

} `n `n

 

Это почти тот же код, что и в разделе 8.4: как мы видели выше, cast() в отчёте classOf заменён на вызов isOf(), как часть отчёта ifmethod, и добавлено условие else с отчётом forward для генерации вызова forward().

Для проверки аргумента вызов forward() происходит через другой селектор. Вероятно, ничего полезного в том, чтобы застрять здесь в рекурсии нет, поэтому если селектор forward() генерирует самого себя, мы останавливаем всё с помощью assert():

 

% forward       // передаём вызов, но не передаём forward

 

`{if `method forward

`t `t assert(0);

`} `{else

`t `t forward(_self, \

          `{if `result void 0, `} `{else & result, `} \

          (Method) `method , " `method ", `%args );

`} `n

 

Дополнительный `{if проверяет, что селектор в конечном счёте возвращает результат, ожидаемый его вызывающей стороной. К этому результату должен будет привести forward(). Общий подход заключается в том, чтобы передать категорию результата в forward(), чтобы каким-то образом его заполнить. Если, однако, наш селектор возвращает void, у нас нет переменной result. В этом случае мы передаём нулевой указатель.

Выглядит так, что вручную можно было бы написать код получше: в некоторых случаях мы могли бы избежать переменной result, присвоения и отдельного оператора return. Однако такая правка излишне усложнила бы отчёты ooc, потому что любой разумный компилятор в любом случае сгенерирует один и тот же машинный код.

classOf — другой отчёт, в котором сделаны значительные изменения. Вызов cast() удалён, но нас интересует вопрос, что происходит, если вызов метода класса должен быть перенаправлен. Давайте посмотрим на селектор, который для new() генерирует ooc:

 

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

    struct Object * result;

    va_list ap;

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

 

    va_start(ap, _self);

    if (class -> new.method) {

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

                                                (_self, & ap);

    } else

        forward((void *) _self, & result, (Method) new, "new",

                                                _self, & ap);

    va_end(ap);

    return result;

}

 

new() вызывается для описания класса, подобного List. Вызов метода класса для чего-то другого, отличного от описания класса, является, вероятно, очень плохой идеей; поэтому чтобы запретить это используется cast(). new принадлежит Class; поэтому делать вызов isOf() необходимости нет.

Давайте предположим, что мы забыли определить начальный Object_new(), то есть что List не унаследовал даже метод new, и поэтому new.method представляет собой нулевой указатель. В этом случае forward() применяется к List. Однако, forward() это динамически компонуемый метод, но не метод класса. Поэтому forward() ищет в описании класса List метод forward, то есть пытается найти ListClass_forward():

 

OOC_Implementation

 

Это совершенно разумно: List_forward() ответственен за все сообщения, которые не понимает aList; ListClass_forward() ответственен за всё то, что List не может обработать. Вот отчёт classOf в etc.rep:

 

`{if `linkage %—

`{if `meta `metaroot

`t const struct `meta * class = classOf(_self); `n

`} `{else

`t const struct `meta * class = ` \

                    (const void *) classOf(_self); `n

`}

`} `{else

`t const struct `meta * class = ` \

                    cast( `metaroot (), _self); `n

`} `n

 

Для динамически компонуемых методов `linkage это %-. В этом случае мы получаем описание класса от classOf() как struct Class, но приводим его к структуре описания класса, что произойдёт как только isOf() проверит тип, чтобы мы могли выбрать подходящий компонент метода.

Для метода класса `linkage определяется как %+, то есть мы переходим ко второй половине отчёта и просто проверяем с помощью cast(), что _self представляет собой по крайней мере Class. Это единственная разница в селекторе для метода класса с перенаправлением.

 

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