微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 Ada 中,placement new 相当于什么?

如何解决在 Ada 中,placement new 相当于什么?

我试图通过编写一组管理动态数组的过程来学习 Ada,但我不知道如何去做。在 C++ 中,我可以轻松地将任意对象放入内存中,如下所示:

#include <new>
#include <iostream>

class object {
  private:
    int value;

  public:
    ~object() noexcept { std::cout << "<> "; }
    object(int value) noexcept : value(value) { std::cout << object::value << ' '; }
};

constexpr auto size = 16;

int main() {
    auto buffer = static_cast<object*>(::operator new(size * sizeof(object)));
    for (auto offset = 0; offset < size; offset++)
        new (buffer + offset) object(offset);
    for (auto offset = 0; offset < size; offset++)
        (buffer + offset)->~object();
    ::operator delete(buffer);
}

在 Ada 中是否有等价于这种类型的“分配 x * sizeof(y) 并初始化 x y 项”操作?

解决方法

是的。 Ada 允许动态内存分配。分配器的主题在 section 4.8 of the Ada Reference Manual 中处理。参考手册中的示例是:

16
Examples of allocators:
17
new Cell'(0,null,null)                          -- initialized explicitly,see 3.10.1
new Cell'(Value => 0,Succ => null,Pred => null) -- initialized explicitly
new Cell                                          -- not initialized
18
new Matrix(1 .. 10,1 .. 20)                      -- the bounds only are given
new Matrix'(1 .. 10 => (1 .. 20 => 0.0))          -- initialized explicitly
19
new Buffer(100)                                   -- the discriminant only is given
new Buffer'(Size => 80,Pos => 0,Value => (1 .. 80 => 'A')) -- initialized explicitly

必须先声明要分配的类型,然后是访问类型才能访问分配的内存。

以下示例对整数数组执行并行加法。数组是动态分配的,因为使用分配器可以分配比在堆栈上分配的数组大得多的数组。

包规范定义了数组类型和访问类型。

package Parallel_Addition is
   type Data_Array is array(Integer range <>) of Integer;
   type Data_Access is access all Data_Array;
   function Sum(Item : in not null Data_Access) return Integer;
end Parallel_Addition;

包体使用两个任务对数组的内容求和。

package body Parallel_Addition is

   ---------
   -- Sum --
   ---------

   function Sum (Item : in not null Data_Access) return Integer is
      task type Adder is
         entry Set (Min : Integer; Max : Integer);
         entry Report (Value : out Integer);
      end Adder;

      task body Adder is
         Total : Integer := 0;
         First : Integer;
         Last  : Integer;
      begin
         accept Set (Min : Integer; Max : Integer) do
            First := Min;
            Last  := Max;
         end Set;
         for I in First .. Last loop
            Total := Total + Item (I);
         end loop;
         accept Report (Value : out Integer) do
            Value := Total;
         end Report;
      end Adder;
      A1  : Adder;
      A2  : Adder;
      R1  : Integer;
      R2  : Integer;
      Mid : constant Integer := (Item'Length / 2) + Item'First;
   begin
      A1.Set (Min => Item'First,Max => Mid);
      A2.Set (Min => Mid + 1,Max => Item'Last);
      A1.Report (R1);
      A2.Report (R2);
      return R1 + R2;
   end Sum;

end Parallel_Addition;

正如你在上面看到的,数组元素的访问没有通过访问类型取消引用数组的特殊语法。这个程序的“主要”程序是

with Parallel_Addition; use Parallel_Addition;
with Ada.Text_IO;       use Ada.Text_IO;
with Ada.Calendar;      use Ada.Calendar;

procedure Parallel_Addition_Test is
   The_Data : Data_Access := new Data_Array (1 .. Integer'Last);
   Start    : Time;
   Stop     : Time;
   The_Sum  : Integer;

begin
   The_Data.all := (others => 1);
   Start        := Clock;
   The_Sum      := Sum (The_Data);
   Stop         := Clock;
   Put_Line ("The sum is: " & Integer'Image (The_Sum));
   Put_Line
     ("Addition elapsed time is " &
      Duration'Image (Stop - Start) &
        " seconds.");
   Put_Line
     ("Time per addition operation is " &
        Float'Image(Float(Stop - Start) / Float(The_Data'Length)) &
        " seconds.");
end Parallel_Addition_Test;

这个程序在我的 Windows 10 计算机上运行时的输出是:

The sum is:  2147483647
Addition elapsed time is  5.628780600 seconds.
Time per addition operation is  2.62111E-09 seconds.
,

在 Ada 中,您可以编写自定义的“Heatmap offset example”(或子池) 在其中重新定义 Allocate 函数以使用缓冲区。然后将访问类型“绑定”到存储池对象。

with System.Storage_Pools;
with System.Storage_Elements;

procedure Main is
   type My_Storage_Pool is new System.Storage_Pools.Root_Storage_Pool with record
      Buffer : System.Storage_Elements.Storage_Array (1 .. 1024);
      Offset : System.Storage_Elements.Storage_Count := 1;
   end record;

   overriding procedure Allocate
     (Self      : in out My_Storage_Pool;
      Address   : out System.Address;
      Size      : System.Storage_Elements.Storage_Count;
      Alignment : System.Storage_Elements.Storage_Count);
   
   overriding procedure Deallocate
     (Self      : in out My_Storage_Pool;
      Address   : System.Address;
      Size      : System.Storage_Elements.Storage_Count;
      Alignment : System.Storage_Elements.Storage_Count) is null;

   overriding function Storage_Size
     (Self : My_Storage_Pool)
      return System.Storage_Elements.Storage_Count is (Self.Buffer'Length);
   
   procedure Allocate
     (Self      : in out My_Storage_Pool;
      Address   : out System.Address;
      Size      : System.Storage_Elements.Storage_Count;
      Alignment : System.Storage_Elements.Storage_Count)
   is
      use type System.Storage_Elements.Storage_Count;
   begin
      Address := Self.Buffer (Self.Offset)'Address;
      Self.Offset := Self.Offset + Size;
   end Allocate;
   
   Pool : My_Storage_Pool;
   
   type Object is null record;
   type Object_Access is access Object with Storage_Pool => Pool;

   X : Object_Access := new Object;
begin
   null;
end Main;

此外,对于 untagged 类型,您可以使用 Address 子句将对象放置到给定地址。这要简单得多。但是这种方式对象初始化不起作用,因此标记类型的对象不会获得虚拟表指针:

Buffer : Stream_Element_Array (1..1024);
Object : My_Object_Type
  with Import,Address => Buffer (Offset)'Address;
--  Here Object'Tag is unassigned and dispatching won't work

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。