Importing C function which accepts array of union
I'm working on an Ada language binding for a C library and stumbled upon a function which expects an array of union values. I tried to used Unchecked_Union
aspect without defining discriminant record but it didn't work because Ada doesn't accept unconstrained element type in array.
In C, function and union are declared something like this:
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);
I'm using GNAT toolchain and running gcc
with -fdump-ada-spec
produced type:
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;
I replaced unsigned discriminant with enum type and it works fine when I use it as a single value, or as an array of unchecked unions with same discriminant value, but I can't figure out the proper way of using different union components in Ada. I do have some ideas to workaround this though, but I'm not sure if they are correct or possible to implement in Ada.
Observations:
- C function internally expects at most 20 items in an array
- There is a companion function with similar signature that uses varargs instead of array of union type, varags are then converted to that union type
Option 1 Use varargs version and generate in Ada several overloaded functions with different count/type combinations. IIUC, this will require 20 * 8 function definitions, not fun at all.
Option 2
Write an import function with definite union type and then somehow cast with Unchecked_Conversion to/from values, i.e. array (Integer range 0..19) of argument(4)
, then convert elements of an array to different types.
Option 3
Take advantage of max array size and allocate (aliased) memory blob of 20 * 64 bytes (union size), write helper procedures which will read/write correct values at correct memory locations, and then pass this blob either as 'Access
or 'Address
to the C function. In this case function parameter would be access my_blob
or just System.Address
.
I'm personally leaning towards option 3, but it'll require a considerable amount of research/work/testing so I'm asking if there are better ways to do that.
P.S. I think it is a defect on Ada side as ARM B.3.3 ยง14/2 clearly states that "All objects of an unchecked union type have the same size", so it should be possible to create an array of unchecked unions without defining discriminant. But I understand that it was done to make code safer to use.