如何在Ada中返回对基类型进行修改的类范围对象的副本

时间:2018-10-12 15:16:35

标签: oop inheritance stack ada

我有一个基本类型,其工作是维护项目列表。它具有非调度功能,可以向其中添加项目以及从中检索项目列表。

从此基本类型派生的类型使用某些调度子程序中的项目。我已经可以通过使基本类型包含项的向量来实现这一点,但是如果可能的话,我希望数组是静态的。这是我到目前为止的内容:

bases.ads:

package Bases is
    type Base (<>) is tagged private; -- I want to hide the size
    type Int_List is array (Positive range <>) of Integer; -- as an example

    function Create return Base; -- returns an empty Base

    function Add_To (This : Base'Class; I : Integer) return Base'Class; -- Append
    function Image (This : Base) return String; -- Dispatching example
    function List (This : Base'Class) return Int_List; -- Get the data for internal use
private
    type Base (Size : Natural) is tagged record
        Ints : Int_List (1 .. Size);
    end record;
end Bases;

bases.adb:

package body Bases is
    function Create return Base is (Size => 0, Ints => (others => 0));
    function Add_To (This : Base'Class; I : Integer) return Base'Class is
        -- This is where I have trouble: "aggregate cannot be of a class-wide type"
        Copy : Base'Class := (This with Size => This.Size + 1, Ints => This.Ints & I);
    begin
        return Copy;
    end Add_To;
    function Image (This : Base) return String is ("BASE");
    function List (This : Base'Class) return Int_List is (This.Ints);
end Bases;

deriveds.ads:

with Bases;
package Deriveds is
    type Derived is new Bases.Base with null record;
    function Create return Derived;
    function Image (This : Derived) return String;
end Deriveds;

deriveds.adb:

with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
package body Deriveds is
    function Create return Derived is (Bases.Create with null record);
    function Image (This : Derived) return String is
        Result : Unbounded_String;
        Ints : Bases.Int_List := This.List;
    begin
        for I in Ints'Range loop
            Result := Result & Integer'Image (Ints (I));
        end loop;

        return To_String (Result);
    end Image;
end Deriveds;

同样,我知道如果我只是删除判别式并使用受控类型来存储数组,那么我可以将副本创建为Copy : Base'Class := This;并对其进行变异,然后再返回它。但是,我觉得应该只有静态内存才可以做到这一点,这是可取的。我唯一想到的其他解决方法是创建另一个标记类型,该类型将是包含列表和Base'Class数据的记录,并且其操作将遮盖Base调度操作,使其通过。

是否无法在Copy中创建Add_To,使其判别式大1,并且仅使用静态内存就具有额外的元素?

2 个答案:

答案 0 :(得分:1)

我认为麻烦在于Bases.Add_To没有一种愉快的标准方法来知道要追加到Base记录(递增Size)以复制特定于类的实际数据的内容在This中。

我想您可以不受限制地转换并通过使用Ada.Tags.Generic_Dispatching_Constructorherehere)来构建记录;但这似乎是个坏主意。

答案 1 :(得分:1)

我知道这需要更多的工作,但是您也可以将Add_To更改为使用Base而不是Base'Class。然后,您必须为具有大于null记录扩展名的所有派生类型重写它,但是您将获得所需的静态数组结果。派生的实现将类似于您实现Create的方式。

示例(我将派生类修改为具有非null记录扩展名,以强制 编译器要求您派生该操作:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure Hello is

    package Bases is
        type Base (<>) is tagged private; -- I want to hide the size
        type Int_List is array (Positive range <>) of Integer; -- as an example

        function Create return Base; -- returns an empty Base

        function Add_To (This : Base; I : Integer) return Base; -- Append
        function Image (This : Base) return String; -- Dispatching example
        function List (This : Base'Class) return Int_List; -- Get the data for internal use
    private
        type Base (Size : Natural) is tagged record
            Ints : Int_List (1 .. Size);
        end record;
    end Bases;

    package body Bases is
        function Create return Base is (Size => 0, Ints => (others => 0));
        function Add_To (This : Base; I : Integer) return Base is
            -- This is where I have trouble: "aggregate cannot be of a class-wide type"
            Copy : Base := (Size => This.Size + 1, Ints => This.Ints & I);
        begin
            return Copy;
        end Add_To;
        function Image (This : Base) return String is ("BASE");
        function List (This : Base'Class) return Int_List is (This.Ints);
    end Bases;

    package Deriveds is
        type Derived is new Bases.Base with  record
            Value : Integer;
        end record;
        function Create return Derived;
        function Add_To(This : Derived; I : Integer) return Derived;
        function Image (This : Derived) return String;
    end Deriveds;

    package body Deriveds is
        function Create return Derived is (Bases.Create with Value => 0);
        function Image (This : Derived) return String is
            Result : Unbounded_String;
            Ints : Bases.Int_List := This.List;
        begin
            for I in Ints'Range loop
                Result := Result & Integer'Image (Ints (I));
            end loop;

            return To_String (Result);
        end Image;
        function Add_To(This : Derived; I : Integer) return Derived is
        begin
            return (Bases.Base(This).Add_To(I) with Value => This.Value);
        end Add_To;
    end Deriveds;

    use Deriveds;

    d0 : Derived := Create;
    d1 : Derived := d0.Add_To(1).Add_To(3);
    d2 : Derived := d1.Add_To(2);

begin
  Put_Line(d2.Image);
end Hello;