Spec-Zone.ru › Ada 2012
Справочник по Ada 2012

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, то соответствующие объявления типов в различных экземплярах обобщенного пакета связаны с различными метками. Для типа с меткой, который является локальным для тела обобщенного пакета, и все его предки (если таковые имеются) также локальны для тела обобщенного пакета, язык не определяет, приводят ли повторные экземпляры тела обобщенного пакета к различным меткам.
Следующий определенный языком библиотечный пакет существует:
package Ada.Tags is
pragma Preelaborate(Tags);
type Tag is private;
pragma Preelaborable_Initialization(Tag);
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 Descendant_Tag(External : String; 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;
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 также возбуждается, если идентифицированный конкретный тип является типом уровня библиотеки, чья метка еще не создана, или если заданная внешняя метка идентифицирует более одного типа, который имеет соответствующий Ancestor и уровень доступности.
Функция 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.
В данном случае prefix 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 Convention => Intrinsic;
pragma Preelaborate(Generic_Dispatching_Constructor);
Tags.Generic_Dispatching_Constructor предоставляет механизм для создания объекта соответствующего типа только из значения метки. Функция Constructor ожидает создания объекта с учетом ссылки на объект типа Parameters.

Динамическая семантика

Метка, связанная с объектом типа с меткой, определяется следующим образом:
  • Метка автономного объекта, компонента или aggregate конкретного типа с меткой T идентифицирует T.
  • Метка объекта, созданного выделением памяти для типа доступа со специфическим целевым типом с меткой T, идентифицирует T.
  • Метка объекта типа с меткой с классом — это метка его выражения инициализации.
END_OF_DOCUMENT_MARKER
  • Тег результата, возвращаемого функцией, тип результата которой — конкретный помеченный тип 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 должен вернуть тег типа, если он существует, внутренний мастер которого является мастером точки вызова функции.
ПРИМЕЧАНИЯ
70 Тип, объявленный с ключевым словом tagged, обычно должен быть объявлен в package_specification, чтобы для него можно было объявить новые примитивные подпрограммы.
71 После создания объекта его тег никогда не изменяется.
72 Типы на уровне класса определяются как имеющие неизвестные дискриминанты (см. 3.7). Это означает, что объекты типа на уровне класса должны быть явно инициализированы (будь то созданные с помощью object_declaration или allocator), и что aggregate должны быть явно квалифицированы конкретным типом, когда ожидаемый тип — на уровне класса.
73 Возможность, предоставляемая Tags.Generic_Dispatching_Constructor, иногда известна как фабрика.

Примеры

Примеры типов помеченных записей:
type Point is tagged
record
X, Y : Real := 0.0;
end record;
type Expression is tagged null record;
-- Компоненты будут добавлены каждым расширением


Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API