#types #rename #ada
#типы #переименовать #ada
Вопрос:
Я пытаюсь создать тип «Путь», который представляет собой вектор точек:
with Ada.Containers.Vectors;
use Ada.Containers;
package Geometry is
type Point is record
North : Integer := 0; -- millimetres
East : Integer := 0;
end record;
function Same_Point (A, B : Point) return Boolean is
(A.North = B.North and then A.East = B.East);
package Path_Points is new Vectors
(Element_Type => Point,
Index_Type => Positive,
"=" => Same_Point);
use Path_Points;
-- works, but poor readability. 'result' is a 'path', not a vector
Result : Vector;
-- What I'd like to do, a type called 'Path':
type Path is new Path_Points.Vector; -- WRONG
end Geometry;
Чтобы, когда я использую геометрию amp;, я мог писать
Result : Path; -- Easy to understand!
Как я могу создать тип ‘Path’, который является просто псевдонимом для Ada.Containers.Vectors.Vector?
Я понимаю, что я мог бы создать в стиле ООП «Путь частного типа» и реализовать все возможности для репликации функций Vectors, но это кажется ужасно неуклюжим.
Комментарии:
1. «»Путь частного типа» и реализовать все возможности для репликации функций Vectors». Нет, вы бы реализовали полный тип и его операции в терминах скрытого экземпляра векторов. Это правильный маршрут, если вы хотите, чтобы геометрия пакета реализовывала абстракцию пути.
2. Почему вы определяете
Same_Point
?"="
определяется дляPoint
и делает то же самое.
Ответ №1:
Vector
тип — это tagged
тип, что означает, что вам нужно расширение типа, а не просто производное:
type Path is new Path_Points.Vector with null record;
Я предполагаю, что компилятор намекнул на это в сообщении об ошибке?
РЕДАКТИРОВАТЬ: прежде чем комментарии ниже затянутся на более длительное обсуждение, позвольте мне прояснить некоторую путаницу:
У типов нет имен в Ada, есть только подтипы, поэтому объявление типа (включая производные типов, которые создают новые типы) на самом деле является объявлением анонимного типа наряду с its first subtype
.
Производные типы наследуют примитивные операции базового типа, как если бы они были объявлены с новым типом. Они совместимы только с базовым типом посредством явных преобразований типов, поэтому вы не можете вызывать операции базового типа с производным типом в качестве параметра без преобразования. Однако в этом редко возникает необходимость, поскольку операции наследуются.
С другой стороны, подтипы работают как переименование базового типа (у них также могут быть ограничения, но это вне этого обсуждения) и не наследуют примитивные операции, но должны полагаться на базовый тип для своих операций. С другой стороны, они совместимы (без преобразования типов) с базовым типом.
таким образом
type Foo is null record;
procedure Process(Item : in out Foo);
является концептуально:
type @anonymous_type is null record;
procedure Process(Item : in out @anonymous_type);
subtype Foo is @anonymous_type;
и
type Bar is new Foo;
является концептуально:
type @derived_type is new @anonymous_type;
procedure Process(Item : in out @derived_type); -- implicit declaration of inherited subprogram
subtype Bar is @derived_type;
в то время как
subtype Baz is Foo;
это просто. Новое имя.
Практические последствия всего этого вступают в игру, когда типы являются производными или подтипами за пределами их собственной области,
with Ada.Strings.Unbounded;
package Foo is
subtype Bar is Ada.Strings.Unbounded.Unbounded_String;
-- Bar is a subtype; no inheritance, must call operations on the base type
Foobar : constant Bar := Ada.Strings.Unbounded.To_Unbounded_String("Foobar");
type Baz is new Ada.Strings.Unbounded.Unbounded_String;
-- Baz is a derived type, inherited operations are directly visible in its scope
Foobaz : constant Baz := To_Unbounded_String("Foobaz");
-- However:
-- This is not legal for a subtype (without a `use` clause):
-- Foobar : constant Bar := To_Unbounded_String("Foobar");
-- This is not legal for a derived type (without an explicit type conversion):
-- Foobaz : constant Bar := Ada.Strings.Unbounded.To_Unbounded_String("Foobaz");
end Foo;
Как теперь становится ясно, производные типы могут значительно сократить ввод, необходимый внутри вашего пакета (даже без use
предложения).
Аналогично, производные типы могут уменьшить требуемую типизацию и количество зависимостей (и, возможно use
, предложений), необходимых для любых пользователей вашего API:
Обратите внимание, как подтип принуждает пользователя к with
области базового типа
with Foo;
with Ada.Strings.Unbounded;
procedure Bar_Test is
Bar : Foo.Bar := Ada.Strings.Unbounded.To_Unbounded_String("Foobar");
-- illegal for a subtype
-- Bar : Foo.Bar := Foo.To_Unbounded_String("Foobar");
begin
null;
end Bar_Test;
Где производный тип не
with Foo; -- Baz is a derived type, inherited operations are visible in Foo
procedure Baz_Test is
Baz : Foo.Baz := Foo.To_Unbounded_String("Foobaz");
-- illegal for a derived type, type conversion needed
-- Baz : Foo.Baz := Ada.Strings.Unbounded.To_Unbounded_String("Foobaz");
begin
null;
end Baz_Test;
Расширение типа определяется как частный случай производного типа, где базовый тип представляет собой тип помеченной записи,
что позволяет нам расширять запись с помощью большего количества элементов:
type Foo is tagged
record
M1 : Natural;
end record;
procedure Process(Item : in out Foo);
type Bar is new Foo with
record
M2 : Natural; -- Bar has both members M1 and M2
end record;
-- implicit declaration of Process for Bar, unless Process is overridden (as for Baz below)
type Baz is new Foo with null record; -- no new members, Baz has only M1
overriding
procedure Process(Item : in out Baz); -- Process is overridden
overriding
Ключевое слово является необязательным, но рекомендуется.
Существует также аналогичное not overriding
, чтобы избежать случайного переопределения
Помеченные типы и расширения типов также позволяют осуществлять динамическую диспетчеризацию для класса типов
Dispatch : Foo'Class := Baz'(Foo with M1 => 1337);
Foo'Class
называется общеклассовым типом и может содержать a Foo
или любой другой тип, укорененный в Foo
(производный / расширенный от Foo
)
Они также позволяют использовать более продвинутые функции, такие как множественное наследование в виде interface
представлений подпрограмм с префиксами (более известных как Объект.Точечная нотация), Управляемые типы и т.д.
Комментарии:
1. Возможно, лучше
subtype Path is Path_Points.Vector
?2. Да, я понял, что это намек, но я не мог найти ответа («вам нужно расширение типа, а не просто производное» — это совсем не очевидно). Спасибо за быстрые ответы, джентльмены, оба очень поучительные, я очень благодарен. Ада сложна для новичков, да? >;-)
3. Можно использовать объявление подтипа как средство для «переименования» другого подтипа.
4. @JimRogers, @SimonWrighht Да, но есть тонкие различия. Производный тип создает новый отдельный тип, поэтому нет путаницы между яблоками и апельсинами, а подтип — нет. И производный тип наследует примитивные операции родительского элемента, тогда как подтип — нет. Это будет иметь влияние при отображении такого типа в API пакета, в большинстве случаев я бы хотел сделать
Foo := Geometry.To_Vector(...);
, а неFoo := Geometry.Path_Points.To_Vector(...);
5. @smirkingman, я пытался намекнуть, что вы должны были указать фактическое сообщение об ошибке в своем вопросе, а не просто
-- WRONG
😉