10.5 Метод respondsTo

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

Как делегат становится  досягаем для объекта? Когда объект Filter создан, он получает объект делегата в виде аргумента. Описание класса Filter.d определяет типы функций для возможных функций обратного вызова и объектных компонентов для хранения указателей:

 

typedef void (* flagM) (void *, void *, char);

typedef int (* nameM) (void *, const void *, const char *);

typedef int (* fileM) (void *, const void *, const char *,

                                                    FILE *);

typedef int (* lineM) (void *, const void *, const char *,

                                                    char *);

typedef int (* wrapM) (void *, const void *, const char *);

typedef int (* quitM) (void *, const void *);

 

% Class Filter: Object {

    Object @ delegate;

    flagM flag;             // обработка флага

    nameM name;             // обработка аргумента имени файла

    fileM file;             // обработка открытого файла

    lineM line;             // обработка буфера строки

    wrapM wrap;             // завершение работы с файлом

    quitM quit;             // завершение завершение со всеми файлами

 

    const char * progname;  // argv[0]

    char ** argv;           // текущий аргумент и байт

 

    char * buf;             // динамический буфер строки

    unsigned blen;          // текущая максимальная длина

%

    int mainLoop (_self, char ** argv);

    const char * argval (_self);

    const char * progname (const _self);

    int doit (_self, const char * arg);

%}

 

К сожалению, ANSI-C не разрешает использовать typedef для определения заголовка функции, но клиентский класс, подобный Wc, может всё же использовать тип функции, чтобы удостовериться, что его функция обратного вызова соответствует ожиданиям Filter:

 

#include "Filter.h"

 

% Wc wc {               // (self, filter, fnm, buf)

%casts

    assert((lineM) wc == wc);

...

 

Результат проверки заведомо истина, но хороший компилятор ANSI-C пожалуется на несоответствие типа, если lineM не будет соответствовать типу wc():

 

In function `Wc_wc’:

warning: comparison of distinct pointer types lacks a cast

 

Мы всё ещё не видим, почему наш filter знает, что вызов wc() обрабатывает входную строку. Filter_ctor() получает объект делегата как аргумент и может установить интересующие filter компоненты:

 

% Filter ctor {

    struct Filter * self = super_ctor(Filter(), _self, app);

 

    self -> delegate = va_arg(* app, void *);

 

    self -> flag = (flagM) respondsTo(self -> delegate, "flag");

    ...

    self -> quit = (quitM) respondsTo(self -> delegate, "quit");

 

    return self;

}

 

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

Возвращаемый указатель функции может быть селектором или самим методом. Если выберем метод, избежим вызова селектора при вызове функции обратного вызова; однако, также лишимся проверки параметра, которую выполняет селектор. Лучше сделать безопасно, чем пожалеть; поэтому respondsTo() возвращает селектор.

Разработка поиска аргумента более трудна. Поскольку respondsTo() является общим методом для всех типов методов, нельзя выполнять проверку типа во время компиляции, но мы уже показали, как делегат может защитить себя. Мы могли бы всё же позволить respondsTo() искать селектор, который, как предполагается, она возвращает, не обращая внимание на проверки типа, то есть поисковым параметром мог бы быть желаемый селектор. Однако, имена селекторов являются частью глобального пространства имён программы, то есть если мы ищем имя селектора, то неявно ограничены подклассами класса, где селектор был представлен. Однако идея не состояла в том, чтобы быть ограниченными аспектами наследования. Поэтому respondsTo() в качестве поискового параметра использует строку.

Осталась проблема связывания строки с динамически компонуемым методом. Логически это может быть сделано в одном из двух мест: где этот метод объявлен в файле описания класса или где он реализован в файле реализации. Так или иначе это работа для ooc, поскольку связь между строковым тэгом и названием метода должна быть сохранена в описании класса, чтобы у respondsTo() была возможность его там найти. Описание класса, однако, создаётся ooc. Мы используем простое расширение синтаксиса:

 

% WcClass: Class Wc: Object {

    ...

%-

line: int wc (_self, const Object @ filter,          \

                const char * fnm, char * buf);

wrap: int printFile (_self, const Object @ filter,   \

                const char * fnm);

quit: int printTotal (_self, const Object @ filter);

%}

 

В файле описания класса, подобного Wc.d, тэг может быть определён как метка, предшествующая динамически компонуемому методу. По умолчанию название метода использовалось бы в качестве тэга. Пустая метка полностью скрывает тэг — в этом случае respondsTo() не сможет найти метод. Тэги относятся к динамически компонуемым методам, то есть они наследуются. Чтобы сделать всё ещё более гибким, тэг может также быть определён как метка в заголовке метода в файле реализации. Такой тэг действителен только для текущего класса.

 

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