Patricia Cortezzi
Patricia Cortezzi

Reputation: 45

Array of strings in Ada

So, I'm coding in Ada for the first time and have to create this pre-populated array of strings, so I can use a random method and call a different value each time.

poolOfWords: constant array(1..50) of string := ("gum","sin","for","cry","lug","bye","fly",
                "ugly", "each","from","work","talk","with","self", "pizza","thing","feign","friend",
                 "elbow","fault", "dirty","budget","spirit","quaint","maiden","escort","pickax",
                 "example","tension","quinine","kidney","replica","sleeper", "triangle","kangaroo",
                 "mahogany","sergeant","sequence","moustache","dangerous","scientist",
                 "different","quiescent", "magistrate","erroneously","loudspeaker",
                 "phytotoxic","matrimonial","parasympathomimetic","thigmotropism");

But when I try to compile I get this error: "unconstrained element type in array declaration".

I Googled all over internet and couldn't find a solution to that. I even tried to use unbounded_string, but didn't work as well. It works fine when I pre-populate an array of integers, floats, but when it come to strings it's not working.

Can someone help me and explain what's going on and how to solve it? I really don't want to declare the array and then populate each of the 50 indexes one by one.

Upvotes: 4

Views: 1413

Answers (2)

Shark8
Shark8

Reputation: 4198

Another option is to use Ada's standard containers. For example:

Package String_Holder is new Ada.Containers.Indefinite_Holders(
   "="          => Ada.Strings.Equal_Case_Insensitive,
   Element_Type => String
  );
Function "+"( Right : String_Holder.Holder ) return String
  renames String_Holder.Element;
Function "+"( Right : String ) return String_Holder.Holder
  renames String_Holder.To_Holder;

Allows you to store in the holder varying sized values of type String, so you could forego any dependence on Ada.Strings.Unbounded_String and use something like:

poolOfWords: constant array(Positive range <>) of String_Holder.Holder := 
  +"gum", +"sin", +"for", +"cry", -- ...
  +"Stuff"
  );

Function Get_Word(Index : Positive) return String is (+poolOfWords(Index)) with Pre => Index in poolOfWords'Range;

Alternatively, you could use Ada.Containers.Indefinite_Vectors like so:

Package String_Vector is new Ada.Containers.Indefinite_Vectors(
   "="          => Ada.Strings.Equal_Case_Insensitive,
   Index_Type   => Positive,
   Element_Type => String
  );

though there is one problem with this in Ada 2012: You can't initialize such a vector directly within the declaration, but can initialize it via function-call:

Function Populate return String_Vector.Vector is
Begin
   Return Result : String_Vector.Vector do
      Result.Append( "some" );
      Result.Append( "data" );
      Result.Append( "HERE" );
   End Return;
End Populate;

Being able to say X : String_Vectors.Vector := ("this", "that", "the other") is in Ada 2020 .

Upvotes: 2

Riccardo B.
Riccardo B.

Reputation: 291

Your problem stems from the fact that String in Ada is an unconstrained type. This means, roughly, that you need to specify "something" in order to have a usable type. In the case of String you need to specify the string length, since a String is just an array of Character. If you say something like

 X : String;

the compiler complains since it does not know the length of X and it does not know how much memory to reserve for X. You need to say something like

  X : String(1..6);

or something like

  X : String := "foobar";

Note that in the second case you do not need to specify the index range since the compiler is able to deduce it from the fact that you use a 6-Character string to initialize X.

This explains why it is illegal to declare an array of just String. When you declare an array the compiler needs to know the size of every entry (all the entries must share the same size, this makes access to a random entry of the array very efficient). If you declare an array of just String, the compiler would not know how much memory to allocate to every entry since the length is unspecified.

Note that this instead would work (disclaimer: I have not an Ada compiler at hand now, so I cannot check the code)

subtype ZIP_Code is String (1..5);
type ZIP_Code_Array is array(Positive range <>) of ZIP_Code;

since now the compiler knows the size of every entry of a ZIP_Code_Array (5 Character).

In your case, however, the strings have all different sizes. I can see two possible solutions

  1. If the maximum length is known in advance you can use the subtype approach shown above and pad the strings with spaces. It depends on your context if this is a suitable solution or not

  2. The approach that I typically use is to create an array of Unconstrained_String that I initialize as follows

    function "+"(X: String) return Unbounded_String
        renames To_Unbounded_String; 
    
    poolOfWords: constant array(1..50) of Unbounded_String := 
        (+"gum",
         +"sin",
         +"for",
          -- and so on...
        );
    

Note the renames that defines the unary + as an alias for To_Unbounded_String. This is just syntactic sugar that allows you to write +"gum" instead of To_Unbounded_String("gum"). Someone cringes at this type of syntactic sugar (and I am not so keen either), but in this case I think it is worth since it makes the code "lighter." It would be nice to have the definition of unary + near its use, just for clarity.

An alternative to the use of Unbounded_String is to use the generic package Bounded_Strings and define poolOfWords as an array of Bounded_String. A Bounded_String can hold strings with variable length, as long as the length is smaller than the bound specified when instantiating the Bounded_Strings package. In order to convert back and forth String you can use the same "rename trick."

My personal preference goes to the Unbounded_String, unless I have some special reason that forces me to use the Bounded_String.

Upvotes: 4

Related Questions