with Interfaces.C.Strings;
procedure CLI is
package C renames Interfaces.C;
procedure Args (args : access C.Strings.chars_ptr_array; num : C.int) is
-- This is really a C function.
begin
null;
end Args;
Total : constant := 2;
CmdLine : aliased C.Strings.chars_ptr_array (0 .. Total);
begin
Args (CmdLine'Access, Total);
end CLI;
Basically, in the above code, I need to create an array of chars_ptr’s containing cli args on the fly, so the size, represented by Total is created by adding the length of 2 arrays (unbounded strings) which will then be converted into this chars_ptr_array.
But it doesn’t compile, it gives me errors:
cli.adb:12:04: warning: aliased object has explicit bounds [enabled by default]
cli.adb:12:04: warning: declare without bounds (and with explicit initialization) [enabled by default]
cli.adb:12:04: warning: for use with unconstrained access [enabled by default]
cli.adb:14:10: error: object subtype must statically match designated subtype
If you want to interface C then an equivalent of char** would be access chars_ptr:
package C renames Interfaces.C;
procedure Args (args : access C.Strings.chars_ptr; num : C.int) is
-- This is really a C function.
begin
null;
end Args;
Total : constant := 2;
CmdLine : C.Strings.chars_ptr_array (0..Total);
begin
Args (CmdLine (CmdLine'First)'Access, Total);
I’m not sure the reason why. As for the two arrays, if I understand your question correctly I think you could use some form of concatenation maybe? (“&” operator).
The compiler message says why, though indirectly. When you declare a constrained array, it has to dope (bounds stored in front of the first element). When you say aliased you hint that you might want to take a pointer to the array. The pointer, if not of a specific type, need bounds. (Ada could use fat pointers with bounds in the pointer, but that is another story). So the compiler warns you that what you are intend to do might be an error. Lo and behold, it is right! Because you indeed make this error later when you apply 'Access to the array in order to get an anonymous access. That target must have bounds which you do not have! The error is spelled in terms of subtypes. An array with no dope (constrained) and an array with a dope (unconstrained) have different subtypes which do not statically match saving you from access violation or worse inside Args later.
in Arg declaration. This is an anonymous access and a bug from the point of view of interfacing C. To C you must pass either pointers to elements or else flat arrays. Here is an example how to make use of a flat array:
with Interfaces.C; use Interfaces.C;
with Interfaces.C.Strings; use Interfaces.C.Strings;
procedure Test is
subtype flat_chars_ptr_array is chars_ptr_array (size_t'Range);
procedure Args (args : flat_chars_ptr_array; num : int) is
-- This is really a C function.
begin
null;
end Args;
Total : constant := 2;
CmdLine : chars_ptr_array (0..Total);
C_CmdLine : flat_chars_ptr_array; -- Flat array mapped to CmdLine
pragma Import (Ada, C_CmdLine);
for C_CmdLine'Address use CmdLine'Address;
begin
Args (C_CmdLine, Total);
end Test;
Here Args takes a flat array which is semantically same as access chars_ptr. You do not need access to array because when interfacing C non-scalars are passed by reference.
But, if really want to crash your program then do this:
procedure Args_Bug (args : access chars_ptr_array; num : int) is
-- This is really a C function.
begin
null;
end Args_Bug;
CmdLine_1 : aliased chars_ptr_array := (0..Total => Null_Ptr);
begin
Args_Bug (CmdLine_1'Access, Total);
Cmd_Line_1 is declared in a way of pretending to be unconstrained. So you can take an access to and surprize C with a proper Ada array. The outcome depends on the implementation. If you used 'Address it would even work, because array address points to the first element. In the case of a pointer, I do not know.