14.2 Реализация |
Предыдущая Содержание Следующая |
К счастью, в главе 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():
Это совершенно разумно: 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. Это единственная разница в селекторе для метода класса с перенаправлением.
|
Предыдущая Содержание Следующая |