我正在为 C 库开发 Ada 语言绑定,偶然发现了一个需要联合值数组的函数。我尝试在没有定义判别记录的情况下使用
Unchecked_Union
方面,但它没有用,因为 Ada 不接受数组中的无约束元素类型。
在 C 中,函数和联合声明如下:
union argument {
int32_t i;
uint32_t u;
fixed_t f; // custom 24.8 floating point type, defined as int32_t
const char *s; // string
struct object *o; // custom object type
uint32_t n; // may be set as id from struct object (o->id)
struct array *a; // custom array type
int32_t h; // file descriptor
};
.. foo(.., union argument *args);
我正在使用 GNAT 工具链并使用
gcc
运行 -fdump-ada-spec
生产类型:
type argument (discr : unsigned := 0) is record
case discr is
when 0 =>
i : aliased x86_64_linux_gnu_bits_stdint_intn_h.int32_t;
when 1 =>
u : aliased x86_64_linux_gnu_bits_stdint_uintn_h.uint32_t;
when 2 =>
f : aliased fixed_t;
when 3 =>
s : Interfaces.C.Strings.chars_ptr;
when 4 =>
o : access object;
when 5 =>
n : aliased x86_64_linux_gnu_bits_stdint_uintn_h.uint32_t;
when 6 =>
a : access array;
when others =>
h : aliased x86_64_linux_gnu_bits_stdint_intn_h.int32_t;
end case;
end record
with Convention => C_Pass_By_Copy,
Unchecked_Union => True;
我用枚举类型替换了无符号判别式,当我将它用作单个值或作为具有相同判别值的未经检查的联合数组时,它工作正常,但我无法弄清楚在 Ada 中使用不同联合组件的正确方法.不过,我确实有一些想法可以解决这个问题,但我不确定它们是否正确或是否可以在 Ada 中实施。
观察:
选项1 使用可变参数版本并在 Ada 中生成几个具有不同计数/类型组合的重载函数。 IIUC,这将需要 20 * 8 个函数定义,一点也不好玩。
选项2 编写一个具有明确联合类型的导入函数,然后以某种方式使用 Unchecked_Conversion 转换为值/从值转换,即
array (Integer range 0..19) of argument(4)
,然后将数组的元素转换为不同的类型。
选项 3 利用最大数组大小并分配(别名)20 * 64 字节(联合大小)的内存 blob,编写辅助程序,在正确的内存位置读取/写入正确的值,然后将此 blob 作为
'Access
或 传递'Address
到 C 函数。在这种情况下,函数参数将是 access my_blob
或只是 System.Address
.
我个人倾向于选项 3,但它需要大量的研究/工作/测试,所以我想问是否有更好的方法来做到这一点。
附言我认为这是 Ada 方面的一个缺陷,因为 ARM B.3.3 §14/2 明确指出“所有未经检查的联合类型的对象都具有相同的大小”,因此应该可以创建一个未经检查的联合数组而无需定义判别式。但我明白这样做是为了让代码更安全地使用。
如果您的记录类型具有默认判别式,那么您应该能够将其声明为组件类型的数组。但是通常最好是在 Ada 端做正确的事情,而尽可能忽略 C 是如何处理事情的。
C 期待一个 32 位值的数组,然后它将以某种方式决定如何解释,所以这就是你应该给它的。
type U32_C is new Interfaces.Unsigned_32 with Convention => C;
subtype Natural_C is Interfaces.C.int range 0 .. Interfaces.C.int'Last;
type Argument_List is array (Natural_C range <>) of U32_C with
Convention => C;
function Foo (...; Argument : in out Argument_List; ...) ... with
Import, Convention => C, Link_Name => ...;
然后您取消选中将 32 位值转换为 U32_C 以创建要传递的数组。