Справочник по Ada 2012
3.9.2 Динамические операции для типов с меткой
Примитивные подпрограммы типа с меткой, подпрограммы, объявленные с помощью formal_abstract_subprogram_declaration, и потоковые атрибуты конкретного типа с меткой, доступные (см. 13.13.2) в конце списка объявления, где объявлен тип, называются динамическими операциями. Динамическую операцию можно вызвать, используя статически определенную управляющую метку, в этом случае тело, которое будет выполнено, определяется во время компиляции. В качестве альтернативы, управляющая метка может быть определена динамически, в этом случае вызов перенаправляется на тело, которое определяется во время выполнения; такой вызов называется динамическим вызовом. Как поясняется ниже, свойства операндов и контекст конкретного вызова динамической операции определяют, как определяется управляющая метка, и, следовательно, является ли вызов динамическим. Полиморфизм во время выполнения достигается, когда динамическая операция вызывается динамическим вызовом.
Статическая семантика
Вызов динамической операции — это вызов, имя или префикс которого обозначает объявление динамической операции. Управляющий операнд в вызове динамической операции типа с меткой T — это операнд, соответствующий формальному параметру типа T или анонимного типа доступа с указанным типом T; соответствующий формальный параметр называется управляющим формальным параметром. Если управляющий формальный параметр является параметром доступа, управляющий операнд — это объект, на который указывает фактический параметр, а не сам фактический параметр. Если вызов обращается к (примитивной) функции с результатом типа T (функция с управляющим результатом), то вызов имеет управляющий результат — контекст вызова может управлять перенаправлением. Аналогично, если вызов обращается к функции с результатом типа доступа, указывающим на T (функция с управляющим результатом доступа), то вызов имеет управляющий результат доступа, и контекст аналогичным образом может управлять перенаправлением.
Имя или выражение типа с меткой являются либо статически помеченными, динамически помеченными, либо помеченные неопределенно, в зависимости от того, когда они используются в качестве управляющего операнда, метка, которая управляет перенаправлением, определяется статически типом операнда, динамически его меткой во время выполнения или по контексту. Квалифицированное выражение или выражение в скобках являются статически, динамически или неопределенно помеченными в соответствии с их операндами. Для других типов имен и выражений это определяется следующим образом:
- Имя или выражение статически помечены, если оно относится к конкретному типу с меткой и, если это вызов с управляющим результатом или управляющим результатом доступа, оно имеет по крайней мере один статически помеченный управляющий операнд;
- Имя или выражение динамически помечены, если оно относится к типу класса или это вызов с управляющим результатом или управляющим результатом доступа, и по крайней мере один динамически помеченный управляющий операнд;
- Имя или выражение помечены неопределенно, если это вызов с управляющим результатом или управляющим результатом доступа, все управляющие операнды (если таковые имеются) помечены неопределенно.
Преобразование типа статически или динамически помечено в зависимости от того, является ли тип, определяемый subtype_mark, конкретным или классовым, соответственно. Для объекта, который обозначен выражением, ожидаемый тип которого является анонимным типом доступа к конкретному типу с меткой, объект динамически помечен, если выражение, игнорируя окружающие скобки, имеет вид X'Access, где X относится к типу класса, или имеет вид new T'(...), где T обозначает подтип класса. В противном случае объект статически или динамически помечен в зависимости от того, является ли назначенный тип типа выражения конкретным или классовым, соответственно.
Правила допустимости
Вызов динамической операции не должен иметь как динамически помеченные, так и статически помеченные управляющие операнды.
Если ожидаемый тип для выражения или имени — какой-то конкретный тип с меткой, то выражение или имя не должны быть динамически помечены, если это не управляющий операнд в вызове динамической операции. Аналогично, если ожидаемый тип для выражения — анонимный тип доступа к конкретному типу с меткой, то объект, на который указывает выражение, не должен быть динамически помечен, если это не управляющий операнд в вызове динамической операции.
В объявлении динамической операции типа с меткой везде, где подтип типа с меткой появляется как подтип профиля (см. 6.1), он должен статически совпадать с первым подтипом типа с меткой. Если динамическая операция переопределяет унаследованную подпрограмму, она должна соответствовать подтипу унаследованной подпрограммы. Конвенция унаследованной динамической операции — это конвенция соответствующей примитивной операции родительского или предкового типа. По умолчанию конвенция динамической операции, которая переопределяет унаследованную примитивную операцию, — это конвенция унаследованной операции; если операция переопределяет несколько унаследованных операций, все они должны иметь одинаковую конвенцию. Явно объявленная динамическая операция не может быть типа Intrinsic.
Выражение по умолчанию для управляющего формального параметра динамической операции должно быть неопределенно помеченным.
Если динамическая операция определяется subprogram_renaming_declaration или экземпляром обобщенной подпрограммы, любой параметр доступа переименованной подпрограммы или обобщенной подпрограммы, который соответствует управляющему параметру доступа динамической операции, должен иметь подтип, исключающий null.
Одна и та же подпрограмма не может быть динамической операцией для двух или более различных типов с меткой.
Явное объявление примитивной подпрограммы типа с меткой должно происходить до того, как тип будет заморожен (см. 13.14). Например, новые динамические операции не могут быть добавлены после существования объектов или значений типа, ни после получения расширения записи из него, ни после тела.
Динамическая семантика
При выполнении вызова динамической операции типа T, значение управляющей метки определяет, какое тело подпрограммы выполняется. Значение управляющей метки определяется следующим образом:
- Если один или несколько управляющих операндов статически помечены, тогда значение управляющей метки статически определяется как метка T.
- Если один или несколько управляющих операндов динамически помечены, тогда значение управляющей метки не определяется статически, а определяется метками управляющих операндов. Если имеется более одного динамически помеченного управляющего операнда, проверяется, что все они имеют одинаковую метку. Если эта проверка не пройдена, возникает Constraint_Error, если вызов не является function_call, имя которого обозначает объявление оператора равенства (предопределенный или пользовательский), который возвращает Boolean, в этом случае результат вызова определен как указание на неравенство, и ни одно subprogram_body не выполняется. Эта проверка выполняется до вычисления любых управляющих операндов с неопределенной меткой.
- Если все управляющие операнды (если таковые имеются) помечены неопределенно, то:
Если вызов имеет управляющий результат или управляющий результат доступа и сам является (возможно, в скобках или с квалификацией) управляющим операндом вложенного вызова динамической операции потомка типа T, то его значение управляющей метки определяется значением управляющей метки этого вложенного вызова;
Если вызов имеет управляющий результат или управляющий результат доступа и (возможно, в скобках, с квалификацией или с декодированием) является выражением assignment_statement, целевой тип которого является типом класса, то значение его управляющей метки определяется целевым типом;
В противном случае значение управляющей метки статически определяется как метка типа T.
При выполнении вызова динамической операции выполняемое действие определяется свойствами соответствующей динамической операции конкретного типа, определенного значением управляющей метки:
- если соответствующая операция явно объявлена для этого типа, даже если объявление находится в частях, то действие включает в себя вызов явного тела операции;
- если соответствующая операция неявно объявлена для этого типа и реализуется с помощью входа или защищенной подпрограммы (см. 9.1 и 9.4), то действие включает в себя вызов этого входа или защищенной подпрограммы, объект-цель задается первым фактическим параметром вызова, а фактические параметры входа или защищенной подпрограммы задаются оставшимися фактическими параметрами вызова, если таковые имеются;
- если соответствующая операция — предопределенный оператор, то действие включает в себя вызов этого оператора;
- в противном случае действие такое же, как действие для соответствующей операции родительского типа или предкового типа, из которого была унаследована операция, за исключением того, что могут применяться дополнительные проверки инвариантов (см. 7.3.2) и проверки пост-условий для всего класса (см. 6.1.1). Если существует более одной такой соответствующей операции, действие относится к операции, которая не является нулевой процедурой, если таковая имеется; в противном случае действие относится к произвольной одной из операций.
ПРИМЕЧАНИЯ
78 Тело, которое будет выполнено для вызова диспатчеризующей операции, определяется тегом; не имеет значения, определяется ли этот тег статически или динамически, и не имеет значения, является ли объявление подпрограммы видимым в месте вызова.
79 Этот подраздел охватывает вызовы диспатчеризующих подпрограмм помеченного типа. Правила для тестов членства помеченного типа описаны в 4.5.2. Управление определением тега для оператор_присваивания описано в 5.2.
80 Вызов диспатчирования может диспатчить в тело, объявление которого не видно в месте вызова.
81 Вызов через значение доступа к подпрограмме никогда не является диспатчеризующим вызовом, даже если значение доступа обозначает диспатчеризующую операцию. Аналогично, вызов, префикс которого обозначает объявление_переименования_подпрограммы, не может быть диспатчеризующим вызовом, если переименование само по себе не является объявлением примитивной подпрограммы.