Справочник по Ada (Ada 2022)
3.9 Размеченные типы и расширения типов
Размеченные типы и расширения типов поддерживают объектно-ориентированное программирование, основанное на наследовании с расширением и полиморфизмом времени выполнения через операции диспетчеризации.
Статическая семантика
Тип записи или частный тип, имеющий ключевое слово tagged в своём объявлении, называется размеченным типом. Кроме того, тип интерфейса является размеченным типом, так же как и тип задачи или защищённого типа, производного от интерфейса (см. 3.9.4). При наследовании от размеченного типа, как и для любого производного типа, могут быть определены дополнительные примитивные подпрограммы, и унаследованные примитивные подпрограммы могут быть переопределены. Производный тип называется расширением его типов предков или просто расширением типа.
Каждый тип расширения также является размеченным типом и представляет собой расширение записи или частное расширение некоторого другого размеченного типа или неинтерфейсного синхронизированного размеченного типа (см. 3.9.4). Расширение записи определяется derived_type_definition с record_extension_part (см. 3.9.1), которая может включать определение дополнительных компонентов. Частное расширение, представляющее собой частичный вид расширения записи или синхронизированного размеченного типа, может быть объявлено в видимой части пакета (см. 7.3) или в части формального параметра обобщения (см. 12.5.1).
Объект размеченного типа имеет связанный (времени выполнения) тег, который идентифицирует конкретный размеченный тип, используемый для создания объекта первоначально. Тег операнда типа размеченного класса T'Class управляет тем, какой тело подпрограммы будет выполнено при применении примитивной подпрограммы типа T к операнду (см. 3.9.2); использование тега для управления тем, какое тело выполнить, называется диспетчеризацией.
Тег конкретного размеченного типа идентифицирует full_type_declaration типа, а для типа расширения достаточно для уникальной идентификации типа среди всех потомков одного и того же предка. Если объявление размеченного типа встречается в generic_package_declaration, то соответствующие объявления типов в разных экземплярах обобщённого пакета связаны с разными тегами. Для размеченного типа, который локален для тела обобщённого пакета, и все его предки (если есть) также локальны для тела обобщения, язык не определяет, приводят ли повторяющиеся экземпляры тела обобщения к различным тегам.
Существует определённый библиотечный пакет Ada:
package Ada.Tags
with Preelaborate, Nonblocking, Global => in out synchronized is
type Tag is private
with Preelaborable_Initialization;
with Preelaborate, Nonblocking, Global => in out synchronized is
type Tag is private
with Preelaborable_Initialization;
No_Tag : constant Tag;
function Expanded_Name(T : Tag) return String;
function Wide_Expanded_Name(T : Tag) return Wide_String;
function Wide_Wide_Expanded_Name(T : Tag) return Wide_Wide_String;
function External_Tag(T : Tag) return String;
function Internal_Tag(External : String) return Tag;
function Wide_Expanded_Name(T : Tag) return Wide_String;
function Wide_Wide_Expanded_Name(T : Tag) return Wide_Wide_String;
function External_Tag(T : Tag) return String;
function Internal_Tag(External : String) return Tag;
function Descendant_Tag(External : String;
Ancestor : Tag) return Tag;
function Is_Descendant_At_Same_Level(Descendant, Ancestor : Tag)
return Boolean;
Ancestor : Tag) return Tag;
function Is_Descendant_At_Same_Level(Descendant, Ancestor : Tag)
return Boolean;
function Parent_Tag (T : Tag) return Tag;
type Tag_Array is array (Positive range <>) of Tag;
function Interface_Ancestor_Tags (T : Tag) return Tag_Array;
function Is_Abstract (T : Tag) return Boolean;
Tag_Error : exception;
private
... -- не определено языком
end Ada.Tags;
... -- не определено языком
end Ada.Tags;
No_Tag — это значение по умолчанию для типа Tag.
Функция Wide_Wide_Expanded_Name возвращает полное расширенное имя первого подтипа конкретного типа, идентифицированного тегом, заглавными буквами, начиная с корневого библиотечного модуля. Результат зависит от реализации, если тип объявлен внутри неименованного block_statement.
Функция Expanded_Name (соответственно, Wide_Expanded_Name) возвращает ту же последовательность графических символов, что и определено для Wide_Wide_Expanded_Name, если все графические символы определены в Character (соответственно, Wide_Character); в противном случае последовательность символов определяется реализацией, но не короче, чем та, что возвращается Wide_Wide_Expanded_Name для того же значения аргумента.
Функция External_Tag возвращает строку, которая должна использоваться во внешнем представлении для заданного тега. Вызов External_Tag(S'Tag) эквивалентен attribute_reference S'External_Tag (см. 13.3).
Строка, возвращаемая функциями Expanded_Name, Wide_Expanded_Name, Wide_Wide_Expanded_Name и External_Tag, имеет нижнюю границу 1.
Функция Internal_Tag возвращает тег, соответствующий заданному внешнему тегу, или возбуждает Tag_Error, если заданная строка не является внешним тегом для какого-либо конкретного типа раздела. Tag_Error также возбуждается, если идентифицированный конкретный тип является типом уровня библиотеки, чьё тег ещё не был создан (см. 13.14).
Функция Descendant_Tag возвращает (внутренний) тег для типа, который соответствует заданному внешнему тегу и является потомком типа, идентифицируемого тегом Ancestor, и имеет тот же уровень доступности, что и идентифицированный предок. Tag_Error возбуждается, если External не является внешним тегом такого типа. Tag_Error также возбуждается, если идентифицированный конкретный тип является типом уровня библиотеки, чьё тег ещё не был создан, или если заданный внешний тег идентифицирует более одного типа, который имеет соответствующего предка и уровень доступности.
Функция Is_Descendant_At_Same_Level возвращает True, если тег Descendant идентифицирует тип, который является потомком типа, идентифицируемого Ancestor, и находится на том же уровне доступности. В противном случае она возвращает False.
В целях динамической семантики функций Descendant_Tag и Is_Descendant_At_Same_Level, тип T2 является потомком типа T1, если он совпадает с T1 или если его родительский тип или один из его предковых типов является потомком типа T1 по этому правилу, даже если в момент объявления T2 одно из наследований в цепочке не видно.
Функция Parent_Tag возвращает тег родительского типа типа, чьё тег — T. Если тип не имеет родительского типа (то есть он не был определён derived_type_definition), то возвращается No_Tag.
Функция Interface_Ancestor_Tags возвращает массив, содержащий тег каждого интерфейсного предкового типа типа, чьё тег — T, за исключением самого T. Нижняя граница возвращаемого массива равна 1, а порядок возвращаемых тегов не определён. Каждый тег появляется в результате ровно один раз. Если тип, чьё тег — T, не имеет интерфейсных предков, возвращается пустой массив.
Функция Is_Abstract возвращает True, если тип, чьё тег — T, является абстрактным, и False в противном случае.
Для каждого подтипа S размеченного типа T (конкретного или универсального), определены следующие атрибуты:
S'Class
S'Class обозначает подтип типа класса (называемого T'Class в этом документе) для класса, укоренённого в T (или, если S уже обозначает подтип типа класса, то S'Class совпадает с S).
S'Class не ограничен. Однако, если S ограничен, то значения S'Class — это только те, которые при преобразовании в тип T принадлежат S.
S'Tag
S'Tag обозначает тег типа T (или, если T — тип класса, тег корневого типа соответствующего класса). Значение этого атрибута имеет тип Tag.
Учитывая префикс X, который является типом размеченного класса (после любого неявного разыменования), определён следующий атрибут:
X'Tag
X'Tag обозначает тег X. Значение этого атрибута имеет тип Tag.
Существует следующая языковая обобщённая функция:
generic
type T (<>) is abstract tagged limited private;
type Parameters (<>) is limited private;
with function Constructor (Params : not null access Parameters)
return T is abstract;
function Ada.Tags.Generic_Dispatching_Constructor
(The_Tag : Tag;
Params : not null access Parameters) return T'Class
with Preelaborate, Convention => Intrinsic,
Nonblocking, Global => in out synchronized;
type T (<>) is abstract tagged limited private;
type Parameters (<>) is limited private;
with function Constructor (Params : not null access Parameters)
return T is abstract;
function Ada.Tags.Generic_Dispatching_Constructor
(The_Tag : Tag;
Params : not null access Parameters) return T'Class
with Preelaborate, Convention => Intrinsic,
Nonblocking, Global => in out synchronized;
Tags.Generic_Dispatching_Constructor предоставляет механизм для создания объекта соответствующего типа только по значению тега. Функция Constructor должна создать объект, используя ссылку на объект типа Parameters.
Динамическая семантика
Тег, связанный с объектом размеченного типа, определяется следующим образом:
- Тег автономного объекта, компонента или aggregate конкретного размеченного типа T идентифицирует T.
- Тег объекта, созданного аллокатором для типа доступа со специфическим указанным размеченным типом T, идентифицирует T.
- Тег объекта класса с тегом соответствует его выражению инициализации.
- Тег результата, возвращаемого функцией, тип результата которой — конкретный тегированный тип T, идентифицирует T.
- Тег результата, возвращаемого функцией с типом результата всего класса, соответствует тегу возвращаемого объекта.
Тег сохраняется при преобразовании типов и при передаче параметров. Тег значения — это тег связанного объекта (см. 6.2).
Ошибка Tag_Error возникает при вызове Descendant_Tag, Expanded_Name, External_Tag, Interface_Ancestor_Tags, Is_Abstract, Is_Descendant_At_Same_Level, Parent_Tag, Wide_Expanded_Name или Wide_Wide_Expanded_Name, если какой-либо переданный тег равен No_Tag.
Экземпляр Tags.Generic_Dispatching_Constructor вызывает ошибку Tag_Error, если The_Tag не представляет конкретного потомка T или если внутренний мастер (см. 7.6.1) этого потомка не является также мастером экземпляра. В противном случае он перенаправляет вызов на примитивную функцию, обозначенную формальным конструктором для типа, идентифицируемого The_Tag, передавая Params, и возвращает результат. Любое исключение, сгенерированное функцией, передаётся вверх.
Ошибка выполнения
Если внутренний тег, предоставленный экземпляру Tags.Generic_Dispatching_Constructor или любой подпрограмме, объявленной в пакете Tags, идентифицирует либо тип, который не является на уровне библиотеки и чья метка не была создана (см. 13.14), либо тип, который не существует в разделе на момент вызова, то выполнение будет ошибочным.
Разрешения реализации
Реализация Internal_Tag и Descendant_Tag может вызвать ошибку Tag_Error, если в разделе на момент вызова функции не существует конкретного типа, соответствующего строке External, переданной в качестве параметра, или если такого типа не существует, внутренний мастер которого является мастером точки вызова функции.
Рекомендации по реализации
Internal_Tag должен возвращать тег типа, если он существует, внутренний мастер которого является мастером точки вызова функции.
ПРИМЕЧАНИЕ 1 Тип, объявленный с помощью ключевого слова tagged, обычно объявляется в спецификации_пакета, чтобы можно было объявить новые примитивные подпрограммы для него.
ПРИМЕЧАНИЕ 2 После создания объекта его тег никогда не меняется.
ПРИМЕЧАНИЕ 3 Типы всего класса определены как имеющие неизвестные дискриминанты (см. 3.7). Это означает, что по правилам 3.7 для объектов с неизвестными дискриминантами объекты типа всего класса нелегальны, если они не инициализированы явно (будь то созданные с помощью объявления_объекта или аллокатора), и что агрегаты нелегальны, если они не квалифицированы явно с конкретным типом, когда ожидаемый тип — тип всего класса.
ПРИМЕЧАНИЕ 4 Возможности, предоставляемые Tags.Generic_Dispatching_Constructor, иногда называют фабрикой.
Примеры
Примеры тегированных записей:
тип Point тегированный
запись
X, Y : Real := 0.0;
конец записи;
запись
X, Y : Real := 0.0;
конец записи;
тип Expression тегированный null_запись;
-- Компоненты будут добавлены каждым расширением
-- Компоненты будут добавлены каждым расширением