Ada常量字符串数组的字面量

3

我有一个在C语言中的大型数组,希望将其移入我的Ada项目中。该数组用于存储资产的文件名,这些资产稍后将被加载。它看起来像:

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

我希望将这个内容移动到一个Ada包体中,但是找不到一个合适的方法来实现。显然,我的第一次尝试是:

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

但是,当然字符串是一种无约束类型,所以Ada不允许这样做。我将其改为使用Unbounded_Strings,虽然有效,但非常丑陋(需要用To_Unbounded_String包装每个字符串)。

是否有任何方法可以创建一个大小在编译时就已知的无约束类型数组,就像这样,还是必须使用无界字符串?


1
另一种方法是使用Interfaces.C.Strings中的机制导入现有数组,包括function Value (Item: in chars_ptr) return String;将数组元素转换为Ada字符串。构建混合语言程序比纯Ada复杂一些,因为编译器无法自动跟踪非Ada依赖项,如果您可以接受传统的Makefile或.gpr项目文件,则可以使用这种方法。 - user1818839
3个回答

3

这个内容有点底层和重复,但或许你可以创建一个小程序(甚至使用Ada语言!)来生成类似的东西。

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;

另请参考使用 To_Unbounded_String 的痛苦最小化的此答案

这就是Ada的方式!与C方式相比,语法笨拙,但编译后的二进制文件非常相似(除了每个C字符串都有一个尾随的\0之外,每个Ada字符串都将携带其判别式)。 - Simon Wright
在C语言中,数组和字符串都不是实际的数据类型,它们只是指针相关的抽象概念。这种抽象的问题在于编译器很少知道数组的长度或被解释为字符串的char数组的长度。可以使用strlen()函数返回字符串的“长度”,但这仅表示第一个\0的位置,而不是数组的大小。长度可能大于或小于数组的大小。它很少是数组的大小,如果是,那么程序就有错误。 - Jim Rogers
@JimRogers,总的来说是对的:但是针对这个特定问题的C语言解决方案(尤其是如果适当地使用了const)不会引起问题。 - Simon Wright
@JimRogers,(1)这个问题特别涉及到字符串字面常量的常量数组,(2)即使是C语言也允许声明数组时不指定NUMBER_OF_FILES,以便从实际提供的元素中自动获取其大小。 - Simon Wright
@SimonWright 虽然 C 允许这样做,但代码示例并不允许。指针数组被声明为 const char *filenames[NUMBER_OF_FILES]。这将在堆栈上为数组分配 NUMBER_OF_FILES 个 char 指针。请注意,如果一些 char 指针是空指针,C 不会发出警告。访问 char 指针数组的代码必须检查特定元素是否包含空指针。如果没有,程序将尝试取消引用空指针。请注意,在这种情况下,任何结果为空指针都将是 const 空指针。 - Jim Rogers
显示剩余3条评论

3

数组不能包含不确定类型的对象。

基本上你有两个选择:

  1. 使用比数组更好的容器。
  2. 封装字符串为定义类型。

所以你可以使用 Ada.Containers.Indefinite_Vectors 代替数组:

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;

所以你可以使用Ada.Strings.Unbounded.Unbounded_String代替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;

1
尚未提到的是,您可以使用一个函数:
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;

在你的代码中使用 Filename (1) 和访问一个数组元素是相同的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接