LordAro
LordAro

Reputation: 1289

Dynamically sized array elements in Ada

So I have the following Ada array declaration buried inside a package body, that eventually gets passed to a C function

declare
    type t_buffer is array (0 .. ARR_SIZE) of Unsigned_32;
    buffer : constant access t_buffer := new t_buffer;
begin

   c_obj.buffer_p := buffer (1)'Address;
   c_obj.buffer_length := Unsigned_64 (buffer'Last);
   for idx in Integer range buffer'Range loop
      buffer (idx) := Unsigned_32 (idx * 4);
   end loop;
end

However, the elements of the array aren't actually always Unsigned_32/uint32_t - it varies between uint8_t, uint16_t, uint32_t & uint64_t, depending on a (runtime) parameter. This means that when it gets read as a (for example) uint16_t array in the C code, the numbers are coming out as a sequence of 0,0,4,0,8,0,... instead of the intended 0,4,8,... when the uint32_t is "split" into 2 different numbers.

Ada doesn't have something approximating dependent types so I can't dynamically create the array type. I'm not sure how I can solve this at all nicely, possibly something to do with making an array of Unsigned_8 and bitshifting as appropriate?

Upvotes: 0

Views: 645

Answers (1)

Jacob Sparre Andersen
Jacob Sparre Andersen

Reputation: 6611

The way Ada works, you have to have four different array types.

But you can encapsulate the selection of the array types in a variant record:

package Variant_Records is

   type Word_Sizes is range 8 .. 64
     with Static_Predicate => Word_Sizes in 8 | 16 | 32 | 64;

   type Data_8_Bit  is mod 2 **  8 with Size =>  8;
   type Data_16_Bit is mod 2 ** 16 with Size => 16;
   type Data_32_Bit is mod 2 ** 32 with Size => 32;
   type Data_64_Bit is mod 2 ** 64 with Size => 64;

   type Array_8_Bit  is array (Positive range <>) of Data_8_Bit;
   type Array_16_Bit is array (Positive range <>) of Data_16_Bit;
   type Array_32_Bit is array (Positive range <>) of Data_32_Bit;
   type Array_64_Bit is array (Positive range <>) of Data_64_Bit;

   type Data_Array (Word_Size : Word_Sizes;
                    Length    : Natural) is
      record
         case Word_Size is
            when  8 => Data_8  : Array_8_Bit  (1 .. Length);
            when 16 => Data_16 : Array_16_Bit (1 .. Length);
            when 32 => Data_32 : Array_32_Bit (1 .. Length);
            when 64 => Data_64 : Array_64_Bit (1 .. Length);
         end case;
      end record;

end Variant_Records;

And then an example of some usage:

with Variant_Records;

procedure Using_Variant_Records is
   use Variant_Records;

   A : Data_Array (Word_Size =>  8, Length => 16);
   B : Data_Array (Word_Size => 64, Length =>  2);
begin
   for I in A.Data_8'Range loop
      A.Data_8 (I) := 2 * Data_8_Bit (I) + 4;
   end loop;

   for I in B.Data_64'Range loop
      B.Data_64 (I) := Data_64_Bit (8 ** I) + 4;
   end loop;

   declare
      D : Data_Array := B;
   begin
      for E of D.Data_64 loop
         E := E * 8;
      end loop;
   end;
end Using_Variant_Records;

Upvotes: 3

Related Questions