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