为接受Union数组的C函数编写Ada绑定的疑难问题求解
我正在为某C库开发Ada语言绑定,遇到一个需要接受Union类型数组的C函数。尝试使用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生成的Ada类型
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;
我将无符号判别式替换为枚举类型,单个值或具有相同判别式的Unchecked_Union数组均可正常使用,但无法找到在Ada中使用不同Union成员的正确方式。我想到了几种 workaround,但不确定其正确性或是否可在Ada中实现。
观察信息
- C函数内部最多接受数组中的20个元素
- 存在一个签名类似的配套函数,使用可变参数而非Union类型数组,可变参数会被转换为该Union类型
方案1
使用可变参数版本,在Ada中生成多个不同参数数量/类型组合的重载函数。据我理解,这需要20*8个函数定义,工作量极大。
方案2
编写导入函数时使用确定的Union类型,然后通过Unchecked_Conversion在值之间进行转换,例如array (Integer range 0..19) of argument(4),再将数组元素转换为不同类型。
方案3
利用数组最大尺寸,分配(aliased)一块20*64字节(Union大小)的内存块,编写辅助过程在正确的内存位置读写正确的值,然后将该内存块以'Access或'Address形式传递给C函数。此时函数参数为access my_blob或仅System.Address。
我个人倾向于方案3,但这需要大量的研究、开发与测试工作,因此询问是否存在更优的实现方式。
附:我认为这是Ada的一个缺陷,因为ARM B.3.3 §14/2明确规定“所有Unchecked_Union类型的对象大小相同”,因此应当可以创建无需定义判别式的Unchecked_Union数组。但我理解这是为了代码安全性而设计的。
内容的提问来源于stack exchange,提问作者servik

