我有一个从套接字读取数据的通用库。请参阅问题末尾的代码清单。
当My_Type
是固定大小类型时,此方法正常,但尝试使用此代码读取变体记录会使用消息STORAGE ERROR
引发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;
答案 0 :(得分: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;
答案 1 :(得分: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;
答案 2 :(得分:0)
您不能以这种方式使用您的代码。这里,消息是在开始之后声明的,这是非法的。
但你可以使用类似的东西:
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
具有可变宽度,则必须知道声明它时使用的实例的大小,并且给出初始值是指定变量大小的一种方法。