Изменить начальное значение по умолчанию в производной записи - PullRequest
0 голосов
/ 05 декабря 2018

Допустим, я создаю следующую запись:

type AnimalSound is (None, Woof);

type Animal is tagged
  record
    sound : AnimalSound := None;
  end record;

И затем я хочу извлечь еще одну запись из этого:

type Dog is new Animal with
  record
    nrPuppies : Integer := 0;
    sound : AnimalSound := Woof;
  end record;

Компилятор выдаст ошибку именного конфликта (конфликтыс объявлением в), что кажется логичным (вы переопределяете что-то, что уже есть), но мне было интересно, есть ли чистый способ иметь другое значение по умолчанию для поля в производном типе записи в Аде.Или, может быть, есть причина, почему это плохая практика и может привести к двусмысленности или аналогичным проблемам?

Ответы [ 3 ]

0 голосов
/ 06 декабря 2018

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

Пример:

with Ada.Text_IO; use Ada.Text_IO;

procedure Hello is

    package Animals is

        type Animal_Sound is (None, Woof);

        -- Interface for extension
        type Animal is limited interface;

        function Get_Sound(Self : Animal) return Animal_Sound is abstract;

        -- Implementation for Composition
        type Default_Animal is new Animal with record
            Sound : Animal_Sound := None;
        end record;

        overriding
        function Get_Sound(Self : Default_Animal) return Animal_Sound
            is (Self.Sound)
        with Inline;

    end Animals;

    package Dogs is

        type Dog is new Animals.Animal with private;

        overriding
        function Get_Sound(Self : Dog) return Animals.Animal_Sound
            with Inline;

        function Get_Number_Of_Puppies(Self : Dog) return Natural;

    private

        use Animals;

        type Dog is new Animal with record
            Number_Of_Puppies : Natural := 0;

            -- Reset the initial value here:
            Implementation    : Default_Animal := (Sound => Woof);
        end record;

        function Get_Sound(Self : Dog) return Animal_Sound
            is (Self.Implementation.Get_Sound);

        function Get_Number_Of_Puppies(Self : Dog) return Natural
            is (Self.Number_Of_Puppies);

    end Dogs;

begin
  Put_Line("Hello, world!");
end Hello;

Вы можете добавить другой интерфейс для Dog и Default_Dog, если хотите продолжить расширение (возможно, изменить другие внутренние переменные).

0 голосов
/ 16 декабря 2018

Я бы предложил что-то вроде следующего.

package Animals is

   type Animal_Sound is (Default, None, Woof, Yelp, Howl, Meow, Oink);

   type Animal is abstract tagged
      record
         Special_Sound: Animal_Sound := Default;
      end record;

   function Default_Sound (Creature: in Animal) return Animal_Sound is abstract;

   function Sound (Creature: in Animal'Class) return Animal_Sound;
end;

package body Animals is

   function Sound (Creature: in Animal'Class) return Animal_Sound is
   begin
      if Creature.Special_Sound /= Default then
         return Creature.Special_Sound;
      else
         return Default_Sound (Creature);
      end if;
   end;

end Animals;

Тогда вы можете добавить собаку следующим образом.

with Animals; use Animals;

package Canine is

   type Puppy_Count is range 0 .. 24;

   type Dog is new Animal with
      record
         Litter_Size: Puppy_Count := 0;
      end record;

   overriding
   function Default_Sound (Pooch: in Dog) return Animal_Sound is (Woof);

end;

Идея состоит в том, что животное определенного класса имеетзвук по умолчанию, но он может иметь специальный звук.У нас есть специальное значение Default, равное Animal_Sound, которое указывает, что звук животного является звуком по умолчанию.Мы определяем абстрактную функцию Default_Sound, которая должна быть переопределена каждым классом животных, и вспомогательную функцию Sound, которая возвращает звук по умолчанию или специальный звук в зависимости от ситуации.

0 голосов
/ 06 декабря 2018

Вы не можете создать экземпляр Dog, не предоставив AnimalSound.

type AnimalSound is (None, Woof) with Default_Value => None; -- for Ada 2012 scalar types demonstration purpose
type Animal is tagged -- class
   record
      sound : AnimalSound; -- Defaults to None
   end record;
type Dog is new Animal with record -- subclass
   nrPuppies : Integer := 0;
end record;
Default_Dog : constant access Dog := new Dog'(sound     => Woof,
                                              nrPuppies => 0);
animal1 : access Animal := new Animal; -- sound defaults to None
animal2 : access Dog := new Dog'(sound     => Woof, -- forced
                                 nrPuppies => 2);
animal3 : access Dog := Default_Dog;
animal4 : access Dog := new Dog'(nrPuppies => 3); -- does not compile: no value supplied for component "sound"

Итак, эта конструкция показывает, что даже если вы зададите значение по умолчанию для данного класса и извлечете из него пользу при создании экземпляра, любой дочерний класснужно было бы дать значение для поля со значением по умолчанию.(Я не проверял, но я верю, что это также относится к полям доступа.)

Вы также можете определить конструктор для класса Dog с 1 arg (nb of puppies).Реализация установит значение для Sound, но концепция будет такой же:

function fancy_default_constructor(nb_puppies : Integer) return not null access Dog is
begin 
    return new Dog'(sound     => Woof, -- forced
                    nrPuppies => nb_puppies);
end fancy_default_constructor;
...