将带有长度字段的 C 灵活数组成员转换为 Ada

Rot*_*er2 4 ada

在编写与 C 代码接口的绑定时,我发现将具有灵活数组成员的结构的大量实例转换为 Ada 时存在问题,例如

struct example {
    size_t length;
    int    body[];
};
Run Code Online (Sandbox Code Playgroud)

有人告诉我,Ada 可以使用可区分的类型来复制类似的行为,但我找不到一种方法来使用该字段length作为判别式,同时保持结构的布局,以便记录可以用于与C代码,类似

type Example (Count : Integer := Length) is record
   Length : Unsigned_64;
   Body   : Integer (1 .. Count);
end record;
Run Code Online (Sandbox Code Playgroud)

有什么方法可以用该数组创建类似的类型吗?我现在一直默认获取该位置的地址并自己声明数组以供使用,有没有更好的方法?提前致谢。

Nik*_*sti 6

下面是 Ada 代码调用 C 代码的示例,将此类对象从 Ada 传递到 C,再从 C 传递到 Ada。C 头文件是 c_example.h:

typedef struct {
    size_t length;
    int    body[];
} example_t;

extern void example_print (example_t *e /* in */);
// Print the contents of e.

extern example_t * example_get (void);
// Return a pointer to an example_t that remains owned
// by the C part (that is, the caller can use it, but should
// not attempt to deallocate it).
Run Code Online (Sandbox Code Playgroud)

C代码是c_example.c:

#include <stdio.h>
#include "c_example.h"

void example_print (example_t *e /* in */)
{
   printf ("C example: length = %zd\n", e->length);

   for (int i = 0; i < e->length; i++)
   {
      printf ("body[ %d ] = %d\n", i, e->body[i]);
   }
}  // example_print

static example_t datum = {4,{6,7,8,9}};

example_t * example_get (void)
{
   return &datum;
}  // example_get
Run Code Online (Sandbox Code Playgroud)

C 到 Ada 的绑定在 c_binding.ads 中定义:

with Interfaces.C;

package C_Binding
is
   pragma Linker_Options ("c_example.o");
   use Interfaces.C;

   type Int_Array is array (size_t range <>) of int
      with Convention => C;

   type Example_t (Length : size_t) is record
      Bod : Int_Array(1 .. Length);
   end record
      with Convention => C;

   type Example_ptr is access all Example_t
      with Convention => C;

   procedure Print (e : in Example_t)
      with Import, Convention => C, External_Name => "example_print";

   function Get return Example_Ptr
      with Import, Convention => C, External_Name => "example_get";

end C_Binding;
Run Code Online (Sandbox Code Playgroud)

测试主程序为flexarr.adb:

with Ada.Text_IO;
with C_Binding;

procedure FlexArr
is

   for_c : constant C_Binding.Example_t :=
      (Length => 5, Bod => (55, 66, 77, 88, 99));

   from_c : C_Binding.Example_ptr;

begin

   C_Binding.Print (for_c);

   from_c := C_Binding.Get;

   Ada.Text_IO.Put_Line (
      "Ada example: length =" & from_c.Length'Image);

   for I in 1 .. from_c.Length loop
      Ada.Text_IO.Put_Line (
         "body[" & I'Image & " ] =" & from_c.Bod(I)'Image);
   end loop;

end FlexArr;
Run Code Online (Sandbox Code Playgroud)

我这样构建程序:

gcc -c -Wall c_example.c
gnatmake -Wall flexarr.adb
Run Code Online (Sandbox Code Playgroud)

这是 ./flexarr 的输出:

C example: length = 5
body[ 0 ] = 55
body[ 1 ] = 66
body[ 2 ] = 77
body[ 3 ] = 88
body[ 4 ] = 99
Ada example: length = 4
body[ 1 ] = 6
body[ 2 ] = 7
body[ 3 ] = 8
body[ 4 ] = 9
Run Code Online (Sandbox Code Playgroud)

所以这似乎有效。然而,Ada 编译器 (gnat) 给了我一些来自 C_Binding 包的警告:

c_binding.ads:14:12: warning: discriminated record has no direct equivalent in C
c_binding.ads:14:12: warning: use of convention for type "Example_t" is dubious
Run Code Online (Sandbox Code Playgroud)

这意味着虽然此接口方法适用于 gnat,但它可能不适用于其他编译器,例如与记录的固定大小部分分开分配 Bod 组件数组的 Ada 编译器(无论此类编译器是否接受 Convention => C对于这种记录类型是有疑问的)。

为了使界面更加可移植,请进行以下更改。在 C_Binding 中,将 Length 从判别式更改为普通组件,并使 Bod 成为固定大小的数组,使用一些最大大小,这里以 1000 个元素为例:

   type Int_Array is array (size_t range 1 .. 1_000) of int
      with Convention => C;

   type Example_t is record
      Length : size_t;
      Bod    : Int_Array;
   end record
      with Convention => C;
Run Code Online (Sandbox Code Playgroud)

在测试主程序中,更改 for_c 的声明以用零填充数组:

   for_c : constant C_Binding.Example_t :=
      (Length => 5, Bod => (55, 66, 77, 88, 99, others => 0));
Run Code Online (Sandbox Code Playgroud)

为了提高速度,您可以让数组中未使用的部分未初始化:“others => <>”。

如果找不到相当小的最大大小,则应该可以将 C 绑定定义为实际大小的通用值。但这变得相当混乱。

请注意,如果所有记录/结构对象都是在 C 端创建的,并且 Ada 端仅读取和写入它们,则绑定中定义的最大大小仅用于索引边界检查,并且可以非常大而不影响内存使用情况。

在此示例中,我使 Ada 端从 1 开始索引,但如果您想让它更类似于 C 代码,您可以将其更改为从 0 开始。

最后,在非歧视的情况下,我建议将 Example_t 设为“有限”类型(“Example_t 类型是有限记录...”),这样您就不能分配该类型的整个值,也不能比较它们。原因是,当C端向Ada端提供Example_t对象时,该对象的实际大小可能小于Ada端定义的最大大小,但Ada赋值或比较会尝试使用最大大小,这可能会使程序读取或写入不应读取或写入的内存。

  • 你的问题仍然不清楚你想做什么。你说“翻译成 Ada 代码”,但你似乎想以某种方式将 Ada 代码与 C 代码连接起来。请更清楚地解释该接口是如何发生的。该结构是跨语言调用的参数吗?如果问题仅在于判别式是最大索引还是长度,我认为 Ada 代码没有理由不能让索引从 1 开始,在这种情况下可以使用“长度”判别式。 (3认同)