Question

I am looking to create a packed record that can hold an array the varies in length from 5 - 50 elements. Is it possible to do this in such a way that the record can be packed with no wasted space? I will know how many elements will be in the array when I go to create the record.

-- the range of the array
type Array_Range_T is Integer range 5 .. 50;

-- the array type
type Array_Type_T is array range (Array_Range_T) of Integer;

-- the record
type My_Record_T (Array_Length : Integer := 5) is
  record
    -- OTHER DATA HERE
    The_Array : Array_Type_T(Array_Length);
  end record;
-- Pack the record
for My_Record_T use
  record
    -- OTHER DATA
    The_Array at 10 range 0 .. Array_Length * 16;
  end record;

for My_Record_T'Size use 80 + (Array_Length * 16);

This obviously won't compile, but shows the spirit of what I am trying to do. If possible I would like to keep the length of the array out of the record.

Thank you!

Was it helpful?

Solution

There really isn't a way in Ada to represent the record the way you're asking for. However, since your concern really isn't with how the record is represented in memory, but rather with how it's transmitted to a socket, you probably don't need to worry about record representation clauses.

Instead, you can define your own Write routine:

procedure Write (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
                 Item   : in My_Record_T);
for My_Record_T'Write use Write;

or, I believe this will work in Ada 2012:

type My_Record_T is record
    ...
end record
with Write => Write;

procedure Write (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
                 Item   : in My_Record_T);

and then the body will look like

procedure Write (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
                 Item   : in My_Record_T) is
begin
    -- Write out the record components, EXCEPT Array_Length and The_Array.
    T1'Write (Stream, Item.F1);  -- F1 is a record field, T1 is its type
    T2'Write (Stream, Item.F2);  -- F2 is a record field, T2 is its type
    ...

    -- Now write the desired data 
    declare
        Data_To_Write : Array_Type_T (1 .. Item.Array_Length)
            renames Item.The_Array (1 .. Item.Array_Length);
                -- I'm assuming the lower bound is 1, but if not, adjust your code
                -- accordingly
    begin
        Array_Type_T'Write (Stream, Data_To_Write);
            -- Note: using 'Write will write just the data, without any bound 
            -- information, which is what you want.
    end;
end Write;

This won't work if the other components need to be packed, though, e.g. if you want to write a byte to the socket that contains one 3-bit record component and one 5-bit record component. If that's necessary, I don't think the built-in 'Write attributes will do that for you; you may need to do your own bit-twiddling, or you could get tricky and define an array of Stream_Elements and use an Address clause or aspect to define an array that overlays the rest of the record. But I wouldn't use the overlay method unless I were 100% certain that the reader at the other end of the socket were an Ada program that uses the exact same type definition.

Note: I haven't tested this.

OTHER TIPS

Not sure I completely understand what you're trying to achieve but can't you do something like this

-- the range of the array
type Array_Range_T is range 1 .. 50;

-- the array type
type Array_Type_T is array (Array_Range_T range <>) of Integer;

Array_Length : constant := 5; --5 elements in the array

-- the record
type My_Record_T is
 record
    -- OTHER DATA HERE
    The_Array : Array_Type_T (1 .. Array_Length);
  end record;

-- Pack the record
for My_Record_T use
 record
-- OTHER DATA
The_Array at 0 range 0 .. (Array_Length * Integer'Size) - 1 ;
end record;

for My_Record_T'Size use (Array_Length * Integer'Size);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top