Запись, содержащая доступ к экземпляру универсального пакета на основе этой записи.

Это действительно неприятная проблема. У меня есть тип записи, который охватывает различные базовые типы, и теперь мне нужно, чтобы он мог хранить вектор (из Ada.Containers.Vectors) в себе! Я думаю, это невозможно, но может ли кто-нибудь дать мне совет, как решить эту проблему по-другому? Чтобы дать вам лучшее представление о том, чем я занимаюсь, вот что не работает:

with Base_Types; use Base_Types;
with Ada.Strings.Wide_Unbounded; use Ada.Strings.Wide_Unbounded;
with Ada.Containers.Vectors;
with Green_Tasks; use Green_Tasks;
with Ada.Unchecked_Deallocation;

package Boxed_Types is

   type String_Ptr is access Unbounded_Wide_String;
   procedure Free_Unbounded_Wide_String is new Ada.Unchecked_Deallocation
     (Object => Unbounded_Wide_String, Name => String_Ptr);
   type Vector_Ptr; -- this won't work

   type Type_T is (T_Null, T_UInt64, T_Text, T_Bool, T_GTask, T_Vector);
   type Item (IType : Type_T := T_Null) is record
      case IType is
         when T_Null   => null;
         when T_UInt64 => UInt64      : UInteger_64;
         when T_Text   => String      : String_Ptr;
         when T_Bool   => Bool        : Boolean;
         when T_GTask  => Green_Task  : Green_Task_Ptr;
         when T_Vector => Item_Vector : Vector_Ptr;     -- error here 
      end case;
   end record;

   package Item_Vectors is new Ada.Containers.Vectors
     (Index_Type   => Natural,
      Element_Type => Item);
   use Item_Vectors;
   type Vector_Ptr is access Vector;
end Boxed_Types;

Это дает мне не столь неожиданную ошибку "недопустимое использование типа до его полного объявления" для Vector_Ptr. Однако я не могу создать экземпляр векторного пакета до того, как объявлю Item, и мне действительно нужен вектор и базовые типы, заключенные в один тип записи. (Это для интерпретатора, который я пишу в свободное время; виртуальная машина должна хранить различные типы в стеке, в гетерогенных массивах, манипулировать ими и т. д..)

Придется ли мне полностью нарушать безопасность типов и возиться с адресом для доступа к конверсиям или есть более чистое решение?


person Eric '3ToedSloth'    schedule 19.05.2013    source источник


Ответы (3)


Вот другая версия, которая хранит элемент (а не его доступ) в векторе. Он работает с использованием наследования, создавая вектор базового типа. Это подразумевает Indefinite_Vector, поскольку размер каждого отдельного компонента заранее неизвестен.

Опять же, скомпилировано, но не проверено.

with Ada.Containers.Indefinite_Vectors;

package Boxed_Base is

   type Base_Item is tagged record
      null;
   end record;

   package Item_Vectors is new Ada.Containers.Indefinite_Vectors
     (Index_Type   => Natural,
      Element_Type => Base_Item'Class);
   use Item_Vectors;

   type Vector_Ptr is access Vector;

end Boxed_Base;

Этот базовый тип имеет свойство, заключающееся в том, что он может храниться в векторе, а управление его хранением осуществляется с помощью Indefinite_Vectors. Теперь мы можем наследовать от него нужные нам характеристики.

with Ada.Strings.Wide_Unbounded; use Ada.Strings.Wide_Unbounded;
with Ada.Unchecked_Deallocation;
with Boxed_Base;

package Boxed_Types is

   type UInteger_64 is new integer;
   type Green_Task_Ptr is access UInteger_64;
   -- these two because original testcase was incomplete

   type String_Ptr is access Unbounded_Wide_String;
   type Type_T is (T_Null, T_UInt64, T_Text, T_Bool, T_GTask, T_Vector);

   type Item (IType : Type_T ) is new Boxed_Base.Base_Item with record
      case IType is
         when T_Null   => null;
         when T_UInt64 => UInt64      : UInteger_64;
         when T_Text   => String      : String_Ptr;
         when T_Bool   => Bool        : Boolean;
         when T_GTask  => Green_Task  : Green_Task_Ptr;
         when T_Vector => Item_Vector : Boxed_Base.Vector_Ptr;    
      end case;
   end record;

end Boxed_Types;

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

Еще одна особенность, может быть упомянута: Indefinite_Vectors может иметь снижение производительности по сравнению с Definite кузенами: если это так, это необходимые затраты, связанные с разнородными размерами объектов, и они будут возникать в той или иной форме, как бы вы ни решали проблему. .

Также можно было бы устранить дискриминант Type_T, создав разные подклассы для каждого типа Предмета; может быть, более чистый дизайн, но на данном этапе это больше рефакторинга, чем вы, вероятно, хотите!

person user_1818839    schedule 19.05.2013
comment
Я проверю все три предложения на предмет их влияния на производительность и отмечу это как ответ, потому что оно дает наиболее подробное объяснение. Все ответы были очень полезны для меня. Большое спасибо! - person Eric '3ToedSloth'; 20.05.2013

Я немного подчистил, компилируется следующее (если заменить green-tasks на null; у меня нет пакета Green_Tasks), но я его не тестировал.

with
Interfaces,
Green_Tasks,
Ada.Containers.Indefinite_Vectors,
Ada.Strings.Wide_Unbounded;

use
Green_Tasks,
Ada.Strings.Wide_Unbounded;

package Boxed_Types is

    type Type_T is (T_Null, T_UInt64, T_Text, T_Bool, T_GTask, T_Vector);
    type Item (IType : Type_T := T_Null) is private; -- Forward declaration;    


private

    type NNA_Item is Not Null Access Item;

    package Item_Vectors is new Ada.Containers.Indefinite_Vectors
      ( Index_Type   => Natural,
        Element_Type => NNA_Item
      );    

    type Item (IType : Type_T := T_Null) is record
        case IType is
        when T_Null   => null;
        when T_UInt64 => UInt64      : Interfaces.Unsigned_64;
        when T_Text   => String      : Unbounded_Wide_String;
        when T_Bool   => Bool        : Boolean;
        when T_GTask  => Green_Task  : Green_Task_Ptr;
        when T_Vector => Item_Vector : Item_Vectors.Vector;
        end case;
    end record;

end Boxed_Types;
person Shark8    schedule 19.05.2013
comment
Это похоже на то, что я бы попробовал, но смысл Indefinite_Vectors в том, чтобы не нуждаться в типах доступа. Что произойдет, если вы создадите экземпляр напрямую с помощью Item? (извините, у меня сейчас нет под рукой компилятора и я не могу его попробовать) - person egilhh; 19.05.2013
comment
Вы не можете; запрещено создавать его экземпляр без преждевременного использования. - person Shark8; 20.05.2013

Я подозреваю, что у Shark8 есть ответ, который вы ищете. И Брайан Драммонд только что опубликовал вариант, похожий на мой!.

Однако в качестве совершенно другого подхода вы можете попробовать (не скомпилированный псевдокод, похожий на ada):

ФАЙЛ Boxed_Types.ads:

type item is tagged null record;

type item_ptr is access all item'Class;

package Item_Vectors is new Ada.Containers.Vectors
  ( Index_Type   => Natural,
    Element_Type => item_ptr -- Actually you may have to wrap this in a record type and possibly make it a controlled type.
  );    

procedure foo (object : in item'Class) is abstract;

ФАЙЛ: boxed_types.uint64.adb (или выберите свое разумное имя):

type T_uint64 is new item with record
  UInt64      : Interfaces.Unsigned_64;
end record;

procedure foo (object : in T_uint64);

Повторите для других элементов исходной записи.

Это означает, что вы можете объявлять объекты класса и использовать динамическую диспетчеризацию:

declare
   Obj : Boxed_Types.Item'Class := ...; 
begin
   Boxed_Types.foo; -- dynamic dispatching
end;

Это должно обойти проблему с Item, содержащим Item, и иметь дополнительное преимущество, заключающееся в том, что не нужно опрашивать тип, прежде чем действовать в его полях данных!

person NWS    schedule 19.05.2013