The website "dmilvdv.narod.ru." is not registered with uCoz.
If you are absolutely sure your website must be here,
please contact our Support Team.
If you were searching for something on the Internet and ended up here, try again:

About uCoz web-service

Community

Legal information

8.4 Стандарт кодирования

8.4 Стандарт кодирования

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

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

 

void move (void * _self, int dx, int dy) {

    struct Point * self = cast(Point, _self);

 

    self -> x += dx, self -> y += dy;

}

 

Если такой метод получает объекты от другого класса, он может всё же вызвать cast(), чтобы удостовериться, что параметры являются тем, чем себя объявляют. Для обработки импорта списка параметров мы ввели в ooc запрос %casts:

 

% move {

%casts

    self -> x += dx, self -> y += dy;

}

 

%casts реализован в etc.rep с помощью отчёта casts; поэтому изменяя этот отчёт мы можем управлять импортом всех объектов. Первоначальная версия показана в разделе 7.4; вот как добавлен cast():

 

% casts                     // реализация запроса %casts

`{()                        // импорт

  `{if `_ _

`t  `const struct `cast * `name = ` \

                       cast( `cast , _ `name ); `n

  `}fi

`}n

`{if `linkage %             // только для статической компоновки

  `%checks

`}fi

 

Замена `_ определена как подчеркивание, если текущий параметр был определён с подчеркиванием в начале, то есть если он находится в текущем классе. Вместо простого присвоения мы вызываем cast(), чтобы выполнить проверку до разыменовывания указателя.

Первый цикл в импорте заботится обо всём объекте в собственном классе метода. Другие объекты проверяются в таких проверках отчёта:

 

% checks // проверка всех других параметров объекта

 

`{()

  `{ifnot `cast ` `{ifnot `_ _

`t cast( `cast , `name ); `n

  `}fi `}fi

`}n

 

Первоначально этот цикл генерировал для всех объектов assert(). Теперь мы можем ограничить наше внимание к тем объектам, которые не находятся в текущем классе. Для них мы генерируем вызов cast(), чтобы удостовериться, что они находятся в надлежащем классе.

Отчёт по разному выполняет casts для методов со статической и динамической компоновкой. Статически компонуемые методы должны сделать свою собственную проверку. casts и checks генерируют локальные переменные для разыменовывания и операторов, чтобы проверять другие объекты, то есть %casts должна использоваться в конце списка локальных переменных, задекларированных вверху тела метода со статической компоновкой.

Динамически компонуемые методы вызываются только через селекторы; поэтому работа по проверке может главным образом быть делегирована им. %casts всё же используется для разыменовывания объектов параметров в текущем классе, но только для инициализации локальных переменных:

 

Circle.dc

 

% Circle draw {

%casts

    printf("circle at %d,%d rad %d\n",

            x(self), y(self), self -> rad);

}

 

Point.c

 

void draw (const void * _self) {

    const struct Point * self = _self;

 

    ...

 

Circle.c

 

static void Circle_draw (const void * _self) {

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

 

    ...

 

Надо быть осторожными: хотя селектор мог бы проверить, принадлежит ли объект текущему классу Point при вызове метода подкласса, подобного Circle_draw(), мы должны проверить, действительно ли объект представляет собой Circle. Поэтому мы позволяем селектору проверить объекты, которые не находятся в текущем классе, и мы позволяем динамически компонуемому методу проверить объекты в своём классе. casts просто опускают вызов checks для методов, которые вызываются через селектор.

Теперь надо модифицировать селекторы. К счастью, все они генерируются отчётом init, но есть несколько особых случаев: селекторы с результатом void не выполняют return для результата фактического метода; селекторы со списком параметров аргументов должны передать указатель на фактический метод. init вызывает отчёт selectors в etc.rep, который в свою очередь делегирует фактическую работу отчёту selector и различным подотчётам. Вот типичный селектор:

 

int differ (const void * _self, const void * b) {

    int result;

    const struct Class * class = classOf(_self);

 

    assert(class -> differ);

    cast(Object, b);

    result = class -> differ(_self, b);

    return result;

}

 

Это сгенерировано отчётом selector в etc.rep (* Реальный отчёт немного сложнее для учёта методов со списком параметров аргументов.):

 

`%header { `n

`%result

`%classOf

 

`%ifmethod

`%checks

`%call

`%return

} `n `n

 

Отчёты result и return определяют и возвращают переменную result, если возвращаемый тип не void:

 

% result        // если необходимо, определяем переменную result

 

`{ifnot `result void

`t `result result;

`}n

% return        // если необходимо, возвращаем переменную result

 

`{ifnot `result void

`t return result;

`}n

 

Отчёт ifmethod проверяет, существует ли желаемый метод:

 

% ifmethod     // проверяем, существует ли метод

 

`t assert(class -> `method ); `n

 

Следует быть поаккуратнее с отчётом classOf: если селектор извлекает метод из Class, для создания подходящего описания класса можно положиться на classOf(),  но для подклассов надо выполнять проверку:

 

`{if `meta `metaroot

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

`} `{else

`t const struct `meta * class = ` \

cast( `meta , classOf(_self)); `n

`} `n

 

Селектор суперкласса аналогичен. Вот типичный пример:

 

int super_differ (const void * _class, const void * _self,

                                        const void * b) {

    const struct Class * superclass = super(_class);

 

    cast(Object, b);

 

    assert(superclass -> differ);

    return superclass -> differ(_self, b);

}

 

Ещё раз, если мы работаем не с Class, то должны проверить результат super(). Вот отчёт из etc.rep:

 

% super-selector     // селектор суперкласса

 

`%super-header { `n

`{if `meta `metaroot // можно использовать super()

`t const struct `meta * superclass = super(_class); `n

`} `{else            // надо вызвать cast

`t const struct `meta * superclass = ` \

                        cast( `meta , super(_class)); `n

`} `n

`%checks

 

`t assert(superclass -> `method ); `n

`t `{ifnot `result void return `} \

      superclass -> `method \

    ( `{() `_ `name `}, `{ifnot `,... ` , app `} ); `n

} `n `n

 

Другие объекты проверяются с помощью checks, также как если бы селектор суперкласса был методом со статической компоновкой.

Благодаря ooc и отчётам мы утвердили безопасный стандарт кодирования для всех методов, которые мы могли бы реализовать. Изменив всех селекторы и согласившись с использованием %casts во всех методах, мы учитываем все объекты, переданные в виде параметров: их указатели проверяются на импорт в вызываемом. Как следствие, результат метода можно не контролировать, потому что ожидается, что пользователь результата применит к нему cast().

Это отражено соглашением использования классов в возвращаемых типах наших методов. Например, в List.d:

 

Object @ addFirst (_self, const Object @ element);

 

Показанный в разделе 7.7 addFirst() возвращает void *. Однако, ooc генерирует:

 

struct Object * addFirst (void * _self, const void * element) {

    struct List * self = cast(List, _self);

 

    cast(Object, element);

 

    ...

    return (void *) element;

}

 

В прикладной программе struct Object представляет собой неполный тип. Таким образом, компилятор ANSI-C проверяет, что результат вызова addFirst() присвоен типу void * (чтобы быть проверенным позже, надо надеяться) или что он передаётся в метод, ожидающий void *, который в соответствии с нашими соглашениями проверит его с помощью cast(). В целом, аккуратно используя классы в типах возврата методов, мы можем использовать компилятор ANSI-C для проверки на маловероятные присвоения. Класс намного более строг, чем void *.

 

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