Матрёшка: контейнер для объектов различных типов
Как всем известно, Ada является языком программирования со строгой типизацией. В общем и целом это хорошо и полезно, однако существуют ситуации, когда необходимо передать некоторое значение тип которого заранее не известен. Несколько таких ситуаций будут рассмотрены позднее, а пока рассмотрим реализацию механизма передачи значений произвольных типов предоставляемого Матрёшкой.
Для передачи значения произвольного типа необходимо использовать тип Holder, объявленный в пакете League.Holders (одно небольшое замечание: в Матрёшке 0.1.x тип называется Value, а пакет — League.Values).
Типовой способ использования объектов типа Holder можно рассмотреть на примере передачи значения строкового типа:
with Ada.Wide_Wide_Text_IO;
with League.Holders;
with League.Strings;
procedure Example_1 is
procedure Put (Item : League.Holders.Holder) is
begin
if Item.Is_Empty then
Ada.Wide_Wide_Text_IO.Put_Line ("Holder is empty");
else
declare
Str : constant League.Strings.Universal_String
:= League.Holders.Element (Item);
begin
Ada.Wide_Wide_Text_IO.Put_Line (Str.To_Wide_Wide_String);
end;
end if;
end Put;
S : constant League.Strings.Universal_String
:= League.Strings.To_Universal_String ("Hello, world!");
H : League.Holders.Holder;
begin
Put (H);
H := League.Holders.To_Holder (S);
Put (H);
end Example_1;
Созданный по умолчанию объект Holder не содержит никакого значения, а функция Is_Empty возвратит True для такого объекта. Поэтому первой строкой вывода примера будет
Holder is empty
А вот созданный с помощью функции To_Holder объект уже содержит значение, Is_Empty возвратит для него False, а с помощью функции Element будет получено собственно хранящееся значение. Функция Element возбудит исключение Constraint_Error в случае отсутствия значения или несовместимости типа значения. Функции To_Holder и Element перегружены для обеспечения возможности обработки значений различных типов данных.
Обработка пользовательских типов данных
Для обеспечения возможности хранения пользовательских типов данных в объектах Holder необходимо настроить один из настраиваемых пакетов:
- League.Holders.Generic_Enumerations — для перечислимых типов;
- League.Holders.Generic_Floats — для вещественных типов;
- League.Holders.Generic_Integers — для целочисленных типов;
- League.Holders.Generic_Holders — для произвольных типов данных.
В настроенном пакете содержатся реализации подпрограмм для установки и получения значения указанного пользовательского типа данных.
Небольшой пример:
with League.Holders.Generic_Integers;
procedure Example_2 is
type My_Integer is range 0 .. 10;
package My_Integer_Holders is
new League.Holders.Generic_Integers (My_Integer);
H : League.Holders.Holder;
V : My_Integer;
begin
H := My_Integer_Holders.To_Holder (10);
V := My_Integer_Holders.Element (H);
end Example_2;
Для стандартных типов данных перечисленные пакеты уже настроены, это:
- League.Holders.Booleans для типа Boolean;
- League.Holders.Floats для типа Float;
- League.Holders.Integers для типа Integer;
- League.Holders.Long_Floats для типа Long_Float;
- League.Holders.Long_Integers для типа Long_Integer;
- League.Holders.Short_Integers для типа Short_Integer.
Типизация
Объекты типа Holder имеют ещё одно важное свойство — ассоциированный тип данных. Он представлется в виде объектов типа Tag объявленного в том же пакете.
Созданный по умолчанию объект Holder не имеет ассоциированного типа равно как и значения. Тип может быть ассоциирован явно с помощью подпрограммы Set_Tag, при этом объект станет типизированным, но всё так же не будет иметь значения. Значение для объекта Holder с ассоциированным типом может быть установлено с помощью подпрограммы Replace_Element, а процедура Clear позволяет удалить значение. Подпрограмма Replace_Element возбудит исключение Constraint_Error при вызове над объектом ассоциированным с несовместимым типом данных. Подпрограмма Clear не меняет ассоциированного типа.
Совместимость типов
В общем и целом ассоциированные типы не совместимы друг с другом. Это означает, что процедура Replace_Element и функция Element возбудят исключение Constraint_Error при вызове с использованием типа отличного от ассоциированного с объектом Holder. Однако, имеет место быть два послабления — для целочисленных и вещественных типов. В пакете League.Holders объявлены два специализированных типа данных Universal_Integer и Universal_Float, которые могут быть использованы для установки/получения значения любого целочисленного или вещественного типа. Тем не менее это не отменяет того факта, что будет возбуждено исключение Constraint_Error при попытке установить значение за пределами допустимого конкретным типом диапазона.
Например, следующий код будет выполнен без возбуждения исключений:
with League.Holders.Integers;
procedure Example_3 is
H : League.Holders.Holder;
V : League.Holders.Universal_Integer;
begin
H := League.Holders.Integers.To_Holder (10);
V := League.Holders.Element (H);
end Example_3;
Заключение
Конечно же сами по себе контейнеры для значений любых типов не представляют большого интереса, однако они широко используются такими модулями Матрёшки как взаимодействие с базами данных, хранение настроек приложения и поддержка моделирования.
Автор: Вадим Годунко
Дата: 22.07.2012
|