Отсутствующие особенности записи Ada

Рассмотрим следующий код C ++:

class Person
{
public:
const std::string Name;
int Weight = 0;
Person(std::string AssignName) : Name(AssignName)
{}
};

void Dinner(Person & ModifyPerson)
{
ModifyPerson.Weight += 2;
}

Имя является константой и сначала инициализируется при создании Person.
Вес можно изменить.

Person Person0("Conny");
Dinner(Person0);

Давайте попробуем это в Аде:

type Person is record
Name : constant String;
Weight : Integer := 0;
end record;

Person0 : Person := Person'(Name => "Abby");

Человекимя всегда должно быть «Эбби», потому что его константа и Вес должен быть изменяемым и 0 по умолчанию.

Не работает, потому что Ада запись не позволяет следующее:

  • Постоянное поле
  • Неуказанный диапазон для типа поля
  • Ограничение списка инициализации, т.е. только назначение название и имеют Вес = 0 по умолчанию.

Как я могу сделать код C ++ в коде Ada?

В C ++ я могу расширить Человек выводя это.
В Аде мы используем помеченную запись для этого.
Но помеченная запись тоже не работает.

Как я могу сделать код C ++ в коде Ada с расширяемыми возможностями Человек?

0

Решение

В Аде вы сначала решаете, что такое Person может и не может сделать, и дизайн интерфейса. Реализация приходит позже.

Насколько я вижу из вашего класса, вы хотите, чтобы человек:

  1. иметь неизменное имя (брак может быть проблемой, но не важно)
  2. Иметь изменяемый вес, инициализированный до 0
  3. иметь возможность набирать или терять вес.
  4. Из обсуждения, быть расширяемым (подкласс)

Теперь запись Ada похожа на Struct, но мы хотим немного большего, и обычный способ обернуть тип данных и его операции вместе — это Package, (Это также имеет некоторую общность с пространством имен C ++, оно также сохраняет главное пространство имен незагроможденным)

Package Person_Pack is
-- tagged for extensibility and other goodies
type Person (Name_Length : Natural) is tagged private;
function Name (P:Person) return String;
function Weight (P:Person) return Natural;
procedure Gain_Weight (P: in out Person; Increment : Integer);
-- Explicit constructor, like "object factory" pattern
function Birth(Name : String) return Person;
private
-- none of your business yet...
end Person_Pack;

И это интерфейс (сохраните его как «person_pack.ads»). Существует метод получения, но нет метода установки для имени, мы гарантировали Person.Nameпостоянство.

Закрытая часть содержит детали объявления типа, так что клиентский код может выделить место для Person и в принципе нет другой причины. Итак, не нужно возиться с полями записи, кроме как с общедоступным интерфейсом … Не думаю, что мне нужно объяснять, почему это хорошо? Так что приватная часть может выглядеть

private
type Person (Name_Length : Natural) is tagged record
Name   : String(1 .. Name_Length);
Weight : Integer := 0;
end record;
end Person_Pack;

Это дискриминируемая запись, поэтому мы можем хранить имя непосредственно в записи, а не использовать Ada.Containers (ближайший эквивалент STL) или даже Unbounded_String. Это деталь реализации, так как она ограничена частной частью, если мы изменим ее позже, не меняя интерфейс, и перекомпилируем, клиентский код все еще работает.

Реализация находится в теле пакета, «person_pack.adb».

Package Body Person_Pack is
function Name (P:Person) return String is
begin
return P.Name;
end Name;

function Weight (P:Person) return Natural is
begin
return P.Weight;
end Weight;

procedure Gain_Weight (P: in out Person; Increment : Integer) is
begin
P.Weight := P.Weight + Increment;
end Gain_Weight;

function Birth(Name : String) return Person is
baby : Person(Name'Length);
begin
baby.Name := Name;
baby.Weight := 0;
return baby;
end Birth;

end Person_Pack;

Использование (сохранить как main.adb):

with Person_Pack; use Person_Pack;

procedure Main is

-- type extension : refactor this into another package...
type Employee is new Person with
record
Salary : Integer;
end record;

function Birth(Name : String) return Employee is
baby : Person(Name'Length) := Birth(Name);
begin
return (baby with Salary => 0);
end Birth;

Abby : Person := Birth("Abigail");
John : Employee := Birth("John");

procedure Dinner (Gourmand : in out Person) is
begin
Gourmand.Gain_Weight(2);
end Dinner;

begin
Dinner(Abby);
end Main;

Компилировать с:

gcc -c -gnat2012 main.adb
gcc -c -gnat2012 person_pack.adb
gnatbind -x main.ali; gnatlink main.ali
7

Другие решения

Члены общедоступных данных осуждаются в любой объектно-ориентированный язык. Так что даже в C ++ я бы ожидал, что люди с солидным опытом проектирования скажут вам поставить Name а также Weight в private: раздел, и добавить методы получения и установки для извлечения и установки полей. Конечно, у вас не будет сеттера для Name так как он неизменен.

То же самое в Аде. Сделать record частный:

    type Person is private;  --or
type Person is tagged private;

function Make_Person (Name : String; Weight : Integer := 0) return Person;

function Get_Name (X : Person) return String;

function Get_Weight (X : Person) return Integer;
procedure Set_Weight (X : in out Person; Weight : Integer);

private

type Person is record --or
type Person is tagged record
Name : Ada.Strings.Unbounded.Unbounded_String;
Weight : Integer;
end record;

Unbounded_String это обычный способ работы со строковыми переменными или полями записи, длина которых может изменяться динамически. Увидеть RM A.4.5 для получения информации о том, как использовать этот пакет, и как конвертировать между Unbounded_String а также String типы. Эти преобразования появятся только в теле пакета, который определяет Person; клиентский пакет, который использует Person не пришлось бы беспокоиться об этом.

Если вы делаете Person tagged record, вы все еще можете использовать «точечную» запись для доступа к функциям:

P : Person;

...

Name : String := P.Get_Name;       -- calls the getter function
Weight : Integer := P.Get_Weight;  -- calls the getter function

P.Set_Weight (P.Get_Weight + 2);

Я не могу ответить на ваше заявление о том, что «запись с тегами также не работает», потому что вы не предоставили никаких подробностей.

5