Функция Generi c, возвращающая карту - PullRequest
2 голосов
/ 12 января 2020

В следующем примере упорядоченная карта создается и возвращается функцией:

with Ada.Containers.Ordered_Maps;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure Main is
   package My_Map is new Ada.Containers.Ordered_Maps
     (Key_Type     => Natural,
      Element_Type => Unbounded_String);

   function Create_Map return My_Map.Map is
      Map_Instance : My_Map.Map;
   begin
      Map_Instance.Insert (1, To_Unbounded_String ("Foo"));
      Map_Instance.Insert (2, To_Unbounded_String ("Bar"));

      return Map_Instance;
   end Create_Map;
begin
   null;
end Main;

Я хотел бы знать, возможно ли преобразовать функцию Create_Map в более обобщенную c один? Следующий код показывает мою идею, но, к сожалению, я действительно не знаю правильных формальных типов ???:

   generic
      type Key_Type is ???;
      type Element_Type is ???;
      type Map_Type is ???;
   function Create_Map_Generic return Map_Type is
      Map_Instance : Map_Type;
   begin
      -- Inserts calls would be based on parsing data ...

      return Map_Instance;
   end;

   package My_Map_One is new Ada.Containers.Ordered_Maps
     (Key_Type     => Natural,
      Element_Type => Unbounded_String);

   function Create_Map_One is new Create_Map_Generic
     (Key_Type     => Natural,
      Element_Type => Unbounded_String,
      Map_Type     => My_Map_One.Map);

   package My_Map_Two is new Ada.Containers.Ordered_Maps
     (Key_Type     => Unbounded_String,
      Element_Type => Positive);

   function Create_Map_Two is new Create_Map_Generic
     (Key_Type     => Unbounded_String,
      Element_Type => Positive,
      Map_Type     => My_Map_Two.Map);

Формальные типы Key_Type и Element_Type указаны дважды (объявление пакета и создание экземпляра) обобщенной функции c). Могут ли они быть извлечены из декларации самого пакета?

Ответы [ 2 ]

4 голосов
/ 12 января 2020

Саймон дал лучший, более полный ответ. Я просто предоставляю альтернативу, в которой пакет карты передается в качестве формального параметра. Для этого вы можете передать пакет как формальный параметр, если его параметры также отправлены как формальные параметры:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps;

procedure Hello is

    generic
        type Key_Type is private;
        type Element_Type is private;
        with function "<"(L,R : Key_Type) return Boolean is <>;
        with function "="(L,R : Key_Type) return Boolean is <>;

        with package Map_Package is new Ada.Containers.Ordered_Maps
            (Key_Type     => Key_Type,
             Element_Type => Element_Type,
             "<"          => "<",
             "="          => "=");
    function Create_Map_Generic return Map_Package.Map;        

    function Create_Map_Generic return Map_Package.Map is
    begin
        return Result : Map_Package.Map do
            null;  -- replace with your input based creation
        end return;
    end Create_Map_Generic;

    -- Just a nonsense type to test out compilation
    type My_Key is null record;
    function "<"(L,R : My_Key) return Boolean is (True);
    function "="(L,R : My_Key) return Boolean is (True);

    package Maps is new Ada.Containers.Ordered_Maps
        (Key_Type     => My_Key,
         Element_Type => Integer);

    function Create is new Create_Map_Generic
        (Key_Type     => My_Key,
         Element_Type => Integer,
         Map_Package  => Maps);

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

В качестве дополнительного дополнительного лакомого кусочка, ЕСЛИ вам все равно, какой ключ и типы элементов находятся внутри функции (я думаю, что вы делаете, но только в случае), тогда вам не нужно указывать их и можете просто сделать:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps;

procedure Hello is

    generic
        with package Map_Package is new Ada.Containers.Ordered_Maps
            (others => <>);
    function Create_Map_Generic return Map_Package.Map;        

    function Create_Map_Generic return Map_Package.Map is
    begin
        return Result : Map_Package.Map do
            null;  -- replace with your input based creation
        end return;
    end Create_Map_Generic;

    -- Just a nonsense type to test out compilation
    type My_Key is null record;
    function "<"(L,R : My_Key) return Boolean is (True);
    function "="(L,R : My_Key) return Boolean is (True);

    package Maps is new Ada.Containers.Ordered_Maps
        (Key_Type     => My_Key,
         Element_Type => Integer);

    function Create is new Create_Map_Generic
        (Map_Package  => Maps);

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

Вы также можете пойти на компромисс в середине, в зависимости от того, что Вы хотите использовать в своей функции создания:

generic
    type Key_Type is private;
    type Element_Type is private;
    with package Map_Package is new Ada.Containers.Ordered_Maps
        (Key_Type     => Key_Type,
         Element_Type => Element_Type,
         others       => <>);
function Create_Map_Generic return Map_Package.Map;       
3 голосов
/ 12 января 2020

Я не уверен, что это отвечает вашему варианту использования, но здесь идет ..

Я не думаю, что вы можете сделать это с помощью обычной функции c; Я думаю, что было бы лучше в качестве универсального c пакета (для объявления карты, например, от Positive до Unbounded_String), содержащего обобщенную c функцию для создания карт с использованием указанной подпрограммы c населения.

Внешний шаблон c:

with Ada.Containers.Ordered_Maps;
generic
   type Key_Type is private;
   type Element_Type is private;
   with function "<" (Left, Right : Key_Type) return Boolean is <>;
   with function "=" (Left, Right : Element_Type) return Boolean is <>;
package Maps_G is
   package Maps is new Ada.Containers.Ordered_Maps
     (Key_Type     => Key_Type,
      Element_Type => Element_Type,
      "<"          => "<",
      "="          => "=");
   subtype Map is Maps.Map;
   generic
      with procedure Populate (The_Map : in out Map);
   function Create_Map return Map;
end Maps_G;

и его тело:

package body Maps_G is
   function Create_Map return Map is
   begin
      return M : Map do
         Populate (M);
      end return;
   end Create_Map;
end Maps_G;

с небольшой тестовой программой:

with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Maps_G;
with Ada.Text_IO;
procedure Test is
   package P_To_UBS_Maps is new Maps_G (Key_Type     => Positive,
                                        Element_Type => Unbounded_String);
   procedure Populate (The_Map : in out P_To_UBS_Maps.Map) is
   begin
      The_Map.Insert (42, To_Unbounded_String ("the answer"));
      The_Map.Insert (1, To_Unbounded_String ("one"));
   end Populate;
   function Create
     is new P_To_UBS_Maps.Create_Map (Populate);
   M : P_To_UBS_Maps.Map := Create;
begin
   for J in M.Iterate loop
      Ada.Text_IO.Put_Line (P_To_UBS_Maps.Maps.Key (J)'Image
                              & " -> "
                              & To_String (P_To_UBS_Maps.Maps.Element (J)));
   end loop;
end Test;

Сборка & run:

$ gnatmake test -f -g
gcc -c -g test.adb
gcc -c -g maps_g.adb
gnatbind -x test.ali
gnatlink test.ali -g
gnatlink: warning: executable name "test" may conflict with shell command
$ ./test
 1 -> one
 42 -> the answer

В этом случае, похоже, нет никакого реального преимущества по сравнению с простым вызовом Populate напрямую.

...