1

我有一个从套接字读取数据的通用库。请参阅问题末尾的代码清单。

My_Type是固定大小的类型时,这可以正常工作,但是尝试使用此代码读取变体记录会引发STORAGE ERROR带有 message的 a object too large

我知道可以通过套接字发送变体记录,因为我有一个示例工作。我认为问题在于我将记录存储为泛型类型。无论变体记录是否具有默认判别式,都会引发异常。有没有办法在这种情况下存储变体记录?

reader_pkg.ads

with Sockets; use Sockets;
with Sockets.Stream_IO; use Sockets.Stream_IO;

generic
  My_Type: private
package Reader_Pkg is
  task type Receive_Task_Type is
    entry Start(FD: Socket_FD);
  end Receive_Task_Type;
end Reader_Pkg;

reader_pkg.adb

package body Reader_Pkg is
  task body Receive_Task is
    Recv_Socket: Socket_FD;
    Recv_Stream: aliased Socket_Stream_Type;
  begin
    select
      accept Start (FD : Socket_FD) do
        Recv_Socket := FD;
        Initialize (Recv_Stream, Recv_Socket);
        declare
          Message: My_Type := My_Type'Input(Recv_Stream'Access); -- STORAGE_ERROR raised here
        begin
          -- Message gets processed here
        end;
      end Start;
    or
      terminate;
    end select;
  end Receive_Task;
end Reader_Pkg;
4

3 回答 3

2

我知道您说无论该类型是否具有默认判别式都会出现问题,但我认为这可能与ARM 3.7(28)有关:

如果判别类型的判别式具有 default_expressions,则允许该类型的不受约束的变量,并且判别式的值可以通过分配给这样的变量来更改。如果没有为判别式提供默认值,则该类型的所有变量都受到约束,要么通过显式约束,要么通过它们的初始值;这种变量的判别式的值在初始化后不能改变。

如果你声明一个类型

type Rec (Len : Natural) is record
   Data : String (1 .. Len);
end record;

那么一个实例,一旦创建,就有一个固定的值Len。但是,如果你说

type Rec (Len : Natural := 4) is record
   Data : String (1 .. Len);
end record;

然后Len可以更改实例中的值(仅通过分配给整个对象),这意味着(对于 GNAT;其他一些编译器以不同的方式执行此操作)编译器必须在堆栈上为最大可能值保留足够的空间;在这种情况下,这意味着为长度为 2^31 - 1 的字符串保留足够的空间。这是行不通的。

如果你在编译时启用了额外的警告,GNAT 会在编译时警告你这个问题(我使用-gnatwa,这是所有常见的警告)。运行时检测应该通过使用来改进-fstack-check

在上述情况下,GNAT 说

sm.adb:20:09: warning: creation of "Rec" object may raise Storage_Error

避免该问题的一种方法可能是告诉编译器您不会分配给该对象:

Message: constant My_Type := My_Type'Input(Recv_Stream'Access);
         ^^^^^^^^

另一个(不希望编译器足够聪明来识别这种情况)是限制最大可能大小:

subtype Length is Natural range 0 .. 1024;
type Rec (Len : Length := 4) is record
   Data : String (1 .. Len);
end record;
于 2013-07-30T05:55:30.947 回答
0

当然是。实际上保存是容易的部分,加载有点棘手,因为判别器是类型的一部分;也就是说,你不能有不受约束的变量。这与编译器无法使用这样的变量是相同的问题,S : String;因为它不知道要为其分配多少内存。

解决这个问题的一种方法是使用访问类型,因为我们可以访问不受约束的类型。下面就是这样一个解决方案。

Pragma Ada_2012;
Pragma Assertion_Policy( Check );

With
System,
Text_Count,
Unchecked_Deallocation,     
Ada.Strings.Fixed,
Ada.Text_IO.Text_Streams;

Procedure Test is

Type Boolean_Array is array (Positive range <>) of Boolean
  with Pack, Component_Size => 1;    

Type Varient_Record( Length : Positive:= 3 ) is record
    Data : Boolean_Array(1..Length):= (others => True);
end record;

Test : constant Varient_Record:=
          (Data => (True, False, False), others => <>);

Function Associate( Name : String;
            Mode : Ada.Text_IO.File_Mode ) 
    Return Ada.Text_IO.File_Type is
Use Ada.Text_IO;
begin
Return Result : File_Type do
    Open(
      File  => Result,
      Name  => Name,
      Mode  => Mode
    );
end return;
End Associate;


Begin
    Ada.Text_IO.Put_Line("Starting Test:");

    SAVE:
    declare
        Test_File : Ada.Text_IO.File_Type:= 
          Associate( "testing.txt", Ada.Text_IO.Out_File);
        Test_Stream : Ada.Text_IO.Text_Streams.Stream_Access:=
          Ada.Text_IO.Text_Streams.Stream( Test_File );
    begin
        Varient_Record'Write( Test_Stream, Test );
            -- Needs an end-line or something to avoid END_ERROR on read.
        String'Write( Test_Stream, ASCII.CR & ASCII.LF );
        Ada.Text_IO.Close( Test_File );
    end SAVE;

    LOAD:
    declare
        Type Access_Varient is Access Varient_Record;
        Procedure Free is new Unchecked_Deallocation(
            Object => Varient_Record,
            Name   => Access_Varient
            );
        Loaded : Access_Varient:= New Varient_Record'(others=><>);
        Test_File : Ada.Text_IO.File_Type:= 
              Associate( "testing.txt", Ada.Text_IO.In_File);
        Test_Stream : Ada.Text_IO.Text_Streams.Stream_Access:=
          Ada.Text_IO.Text_Streams.Stream( Test_File );
    begin
        Varient_Record'Read( Test_Stream, Loaded.All );

        Ada.Text_IO.Put_Line( "Test and Loading are" &
              (if Test /= Loaded.All then " NOT " else " ") & 
            "the same." );

        Free(Loaded);

    Ada.Text_IO.Close( Test_File );
    End LOAD;


    Ada.Text_IO.Put_Line("Testing complete.");
End Test;
于 2013-07-29T14:09:53.303 回答
0

您不能以这种方式使用您的代码。这里,message 在 begin 之后声明,这是非法的。

但是,您可以使用以下内容:

declare
  Recv_Socket : Socket_FD;
  Recv_Stream : aliased Socket_Stream_Type;
begin
  -- maybe init your stream here
  declare
    Message : My_Type := My_Type'Input(Recv_Stream'Access);
  begin
    DoSomethingWithMessage(Message);
  end;
end;

如果My_Type具有可变宽度,则在声明它时必须知道正在使用的实例的大小,并且给出初始值是指定变量大小的一种方法。

于 2013-07-29T14:11:08.480 回答