Ada constant array of string literals

2019-07-25 03:06发布

问题:

I have a large array in C that I wish to move into my ada project. That array is used to store filenames for assets which will later be loaded. It looks something like:

const char *filenames[NUMBER_OF_FILES] = {
    /* file[0] */ "res/0.file",
    /* ... */
    /* file[n] */ "res/some_more/complicated/file.name"
};

I want to move this into an ada package body, but can't find a decent way to do it. Obviously my first attempt was:

filenames : constant array (File_Index) of String := (
    index_0 => "res/0.file",
    --  ...
    index_n => "res/some_more/complicated/file.name"
);

But of course String is an unconstrained type, so Ada won't allow that. I switched it to use Unbounded_Strings, which worked, but was very ugly (having to wrap each string with To_Unbounded_String.

Is there any way to make an array of unconstrained types whose size will be known at compile time like this, or do I have to use unbounded strings?

回答1:

It’s a bit low-level and repetitive, but perhaps you can create a little program (maybe even in Ada!) to generate something like

with Ada.Text_IO; use Ada.Text_IO;
procedure Lambdabeta is
   F1 : aliased constant String := "res/0.file";
   F2 : aliased constant String := "res/some_more/complicated/file.name";
   type Strings is array (Positive range <>) of access constant String;
   Filenames : constant Strings := (F1'Access,
                                    F2'Access);
begin
   for J in Filenames'Range loop
      Put_Line (Filenames (J).all);
   end loop;
end Lambdabeta;

See also this answer on minimising the pain of using To_Unbounded_String.



回答2:

Arrays can't contain objects of indefinite types.

Basically you have two options:

  1. Use another container than an array.
  2. Encapsulate the strings in a definite type.

So you could use Ada.Containers.Indefinite_Vectors instead of an array:

with Ada.Containers.Indefinite_Vectors;

package Settings is
   ------------------------------------------------------------------
   --  You may want to put this block in a separate package:
   package String_Vectors is
     new Ada.Containers.Indefinite_Vectors (Index_Type   => Positive,
                                            Element_Type => String);
   function "+" (Left, Right : String) return String_Vectors.Vector
     renames String_Vectors."&";
   function "+" (Left  : String_Vectors.Vector;
                 Right : String) return String_Vectors.Vector
     renames String_Vectors."&";
   ------------------------------------------------------------------

   File_Names : constant String_Vectors.Vector :=
                  "/some/file/name" +
                  "/var/spool/mail/mine" +
                  "/etc/passwd";
end Settings;

So you could use Ada.Strings.Unbounded.Unbounded_String instead of String:

with Ada.Strings.Unbounded;

package Settings_V2 is
   function "+" (Item : in String) return Ada.Strings.Unbounded.Unbounded_String
     renames Ada.Strings.Unbounded.To_Unbounded_String;
   type String_Array is array (Positive range <>)
     of Ada.Strings.Unbounded.Unbounded_String;

   File_Names : constant String_Array :=
                  (+"/some/file/name",
                   +"/var/spool/mail/mine",
                   +"/etc/passwd");
end Settings_V2;


回答3:

What has not yet been mentioned is that you can just use a function:

subtype File_Index is Integer range 1 .. 3;
function Filename (Index : File_Index) return String is
begin
   case Index is
   when 1 => return "res/0.file";
   when 2 => return "res/1.file";
   when 3 => return "res/some_more/complicated/file.name";
   end case;
end Filename;

Using Filename (1) in your code is identical to accessing an array element.