How would I create a Record type containing a Vector and initialize it?

In FreePascal (Delphi - more or like) I can do the following:

TRangeDescr = Record
   Octets: Array Of SmallInt;   // Unconstrained array
   Color:  UnicodeString;
End;

PRIVATE_NETS: Array [0..2] Of TRangeDescr =
(
   (Octets: [10];       Color: COLOR_PRIVATE1),
   (Octets: [172,  16]; Color: COLOR_PRIVATE2),
   (Octets: [172, 168]; Color: COLOR_PRIVATE3)
);

How would I accomplish that in Ada?

As I take it, unconstrained Arrays are not allowed in Records, Vectors might be(?), but (being new to Ada) Iā€™m completely lost hereā€¦

I believe this would work, but Iā€™m not running it through a compiler to doublecheck:

type T_Range_Descr (Count : Integer) is record
   Color: String;
   Octets: array (1 .. Count) of Integer;
end record;

This is called a discriminated record.

As for initialization, Ada has a uniform syntax for this.

A : T_Range_Descr := ("blue", (1, 2));

I may have made some minor mistakes, but this is roughly correct, at the least. Notice that the variable A neednā€™t have a constraint, as it can be inferred from the initialization expression.

Actually, I donā€™t believe that works for records, but I know it works for arrays.

Thx for your quick reply, but - unfortunately - this is not what Iā€˜m looking for. As you can see from the initialization, the Array contains three Records, each of which contains an unconstrained Array with a different number of elements (which is crucial for the program).

Also, I donā€˜t want to add a length field, or a semaphore (would neither be required in Delphi, nor in C++ tooā€¦).

Oh, I think the array will need to hold access types which point to the records, in that case. Alternatively, each record can hold an access type which points to the subarray.

I would use a variant record with the discriminant being the array length and then use Ada.Containers.Indefinite_Vectors to hold the variant records like an array. Example below, but I had to use dummy types to get a complete compileable example for you.

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Vectors;

procedure jdoodle is

    type Octet_Array is array(Positive range <>) of Integer;
    
    type Range_Description
        (Octet_Count : Positive;
         Name_Length : Positive)
    is record
        Octets : Octet_Array(1..Octet_Count);
        Name   : String(1..Name_Length);
    end record;
    
    package Description_Vectors is new Ada.Containers.Indefinite_Vectors
        (Index_Type   => Positive,
         Element_Type => Range_Description);
    subtype Description_Vector is Description_Vectors.Vector;
    
    use type Description_Vector; -- make "&" visible
    
    Private_Nets : constant Description_Vector := Description_Vectors.Empty_Vector
        & (Octet_Count => 1, Name_Length => 14, Octets => (1 => 10),  Name => "COLOR_PRIVATE1")
        & (Octet_Count => 2, Name_Length => 14, Octets => (172, 16),  Name => "COLOR_PRIVATE2")
        & (Octet_Count => 2, Name_Length => 14, Octets => (172, 168), Name => "COLOR_PRIVATE3");

begin
    for Net of Private_Nets loop
        Put_Line(Net.Name);
    end loop;
end jdoodle;

In Ada 2022:

Private_Nets : constant Description_Vector := [
   (Octet_Count => 1, Name_Length => 14, Octets => (1 => 10),  Name => "COLOR_PRIVATE1"),
   (Octet_Count => 2, Name_Length => 14, Octets => (172, 16),  Name => "COLOR_PRIVATE2"),
   (Octet_Count => 2, Name_Length => 14, Octets => (172, 168), Name => "COLOR_PRIVATE3")
];

@Verisimilitude: the compiler complains: ā€œanonymous array not allowed as componentā€

@Lucretia, @Jere: this is pretty much code for just a ā€œsmallā€ data descriptionā€¦

Thatā€™s ok. It should still work for you I believe?

Provide the full compiler error message for me.

@jere
It does what it is supposed to :wink:
Thank for your help!

@Verisimilitude
The exact output is:

gcc -c -gnat2022 splitcidr.adb
splitcidr.adb:57:12: error: anonymous array not allowed as component
gnatmake: ā€œsplitcidr.adbā€ compilation error

Line 57:

Octets: Array (1 ā€¦ Count) of Short_Integer;

Could the definition in line 56:

Color: String

Itā€™s definitely the string element.

type Byte_List is array (Positive range <>) of Unsigned_8);

type Range_Descr (Num_Bytes : Positive) is record
   Octet : Byte_List (1 .. Num_Bytes);
   Color : Unbounded_String;
end record;

package Range_Lists is new Ada.Containers.Indefinite_Vectors (Index_Type => Positive, Element_Type => Range_Descr);
subtype Range_List is Range_Lists.Vector;
use type Range_List;

Private_Net : Range_List := Range_Lists.Emtpy_Vector                                      &
                            (Num_Bytes => 1, Octet => (1 =>  10), Name => Color_Private1) &
                            (Num_Bytes => 2, Octet => (172,  16), Name => Color_Private2) &
                            (Num_Bytes => 2, Octet => (172, 168), Name => Color_Private3) );

Both are wrong: a component must be defined by a subtype name, so the anonymous array type is illegal, and must be definite, so the use of String is illegal.

Is there a reason people are avoiding using Vectors? They would work for the indicated task and would also be modifiable, in case octets need to be added.

I prefer to use the oldest Ada standard suitable, which is usually Ada 1995. Thereā€™s no need to use Vectors for something so simple.

I think the following code is closer to what youā€™d like (and to what is in the FreePascal code):

with Ada.Containers.Vectors;
with Ada.Strings.Unbounded;

procedure Test_Vect is

  use Ada.Strings.Unbounded;

  type Byte is mod 2 ** 8;

  package Byte_Vectors is
    new Ada.Containers.Vectors (Positive, Byte);

  type Range_Descr is record
     Octets : Byte_Vectors.Vector;
     Color  : Unbounded_String;
  end record;

begin
  null;
end;

The FreePascal dynamic array corresponds an Ada container vector.
An unconstrained type is something different that exists perhaps only in Ada. The objects have fixed dimensions, but their type hasnā€™t. They are by default stack-allocated. See the type String as the most basic example.

1 Like

Thx for all your answers so far. Currently I have ended up with this ā€œproggyā€:

pragma Ada_2022; 

with Ada.Text_IO;
with Ada.Containers.Vectors;
with Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO;

use Ada.Text_IO;
use Ada.Strings.Unbounded;This text will be hidden

procedure SplitCIDR is
   type Byte is Mod 2 ** 8;

   type Bytes_Array is Array(0..3) Of Byte;

   type Info_Record is Record
      Mask:      Bytes_Array;
      Net:       Bytes_Array;
      broadcast: Bytes_Array;
   End Record;

   package Byte_Vectors is new Ada.Containers.Vectors(Positive, Byte);

   type Range_Descr is Record
      Octets: Byte_Vectors.Vector;
      Color:  Unbounded_String;
   End Record;

   COLOR_NORMAL:   Unbounded_String := To_Unbounded_String("<esc>[m");       -- Side problem: HOW WOULD I SPECIFY "<esc>"?
   COLOR_VERSION:  Unbounded_String := To_Unbounded_String("<esc>[33m");
   COLOR_PRIVATE1: Unbounded_String := To_Unbounded_String("<esc>[33;1m");
   COLOR_PRIVATE2: Unbounded_String := To_Unbounded_String("<esc>[32;1m");
   COLOR_PRIVATE3: Unbounded_String := To_Unbounded_String("<esc>[31;1m");

   PRIVATE_NETS: Array(0 .. 2) Of Range_Descr :=
   (
      (Octets => [10],       Color => COLOR_PRIVATE1),
      (Octets => [172,  16], Color => COLOR_PRIVATE2),
      (Octets => [172, 168], Color => COLOR_PRIVATE3)
   );

begin
   Put_Line(PRIVATE_NETS(1).Color'Image);
   Put_Line(PRIVATE_NETS(1).Octets(0));    -- HOW WOULD I OUTPUT THIS?

   -- Side problem:
   -- Using Bounded_String gives me:
   -- x.adb:44:04: error: no candidate interpretations match the actuals:
   -- x.adb:44:04: error: missing argument for parameter "Item" in call to "Put_Line" declared at a-textio.ads:555
   -- x.adb:44:28: error: expected type "Standard.String"
   -- x.adb:44:28: error: found private type "Ada.Containers.Vectors.Constant_Reference_Type" from instance at line 22
   -- x.adb:44:28: error:   ==> in call to "Put_Line" at a-textio.ads:566
end;

As I said, Iā€™m new to Ada, so I seem to have issues with some very basic problems, namely:

  • How to use (#27 in Delphi, \x27 in C) in a [[un]bounded] string?
  • How do I print the first Octet?
  • How do I use Bounded_Strings in place of Unbounded_Strings?

Sorry for the many simple questions, and thx for you patience (so far) :wink:

For #1:
In Ada, you can transform any number sequence to a character usint the ā€œValā€ attribute:

Value : Character := Character'Val(27)

For #2:
Depends on how you want to print it. Ada.Text_IO has a generic package Modular_IO that you can use to instantiate and print numbers to a string. Adaā€™s default string format for numbers not in base 10 is ## (EX: ā€œ16#F1#ā€). Modular_IO has a Put operation where you supply the output string, the number, and what base you want to print in. If that default format doesnā€™t work for you, you can make a function to customize it:

with Ada.Text_IO; use Ada.Text_IO;

procedure jdoodle is

    type Byte is Mod 2 ** 8;
    
    function To_Hex(Value : Byte) return String is
        Result : String := "16#00#";  --Preformat to get the correct length
        Pound_Position : constant Positive := 3;
        
        package IO is new Ada.Text_IO.Modular_IO(Byte);
        use IO;
    begin
        Put(To => Result,
            Item => Value,
            Base => 16);
        case Result(Pound_Position) is
            when '#' =>
                -- If the pound is in the original position, this is a
                -- two character string
                return Result(Pound_Position + 1 .. Pound_Position + 2);
            when '6' =>
                -- If it is the '6' from "16", then the string is only
                -- one character long
                return Result(Pound_Position + 2 .. Pound_Position + 2);
            when others =>
                -- Should never get here, but testing out
                raise Constraint_Error;
        end case;
    end To_Hex;
begin
    for Value in Byte'Range loop
        Put_Line(To_Hex(Value));
    end loop;
end jdoodle;

You can further adjust the logic to do things like put in leading zeros, etc.

For #3:
You can mostly just copy/paste replace the ā€œunboundedā€ with ā€œboundedā€ everywhere, then in your record you add the maximum capacity:

type Range_Descr is Record
      Octets: Byte_Vectors.Vector;
      Color:  Bounded_String(80);
End Record;

There might be a few other small niche things, but that should get you started.

1 Like

FWIW You might want to look at this page. In your case, itā€™s Ada.Characters.Latin_1.ESC? (Correct me if Iā€™m wrong.) IMHO itā€™s a better idea to use meaningful symbols than numbers.

2 Likes