Help with a compiler error

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

I get the same error in my actual code.

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);

The previous code I had was this:

CmdLine : aliased C_Strings.chars_ptr_array      := (+Arg1, +Arg2, +Arg3, +Arg4, +Arg5, +Arg6, +Arg7, +Arg8);

Where + just returns the underlying C.Strings.chars_ptr which is what is being stored within the array.

I now construct that array. It’s an array of c strings.

I was able to get it to compile by changing the object declaration (Based on the warning message you posted):

CmdLine : aliased C.Strings.chars_ptr_array := (0 .. Total => <>);

See if that gets you what you need.

1 Like

Cheers, that worked.

Why can that array not be set up that way?

Also, seeing that other array question with the new for expression.

Is there a way to use that for expression to take two arrays to create this new one?

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.

1 Like
  1. It’s got bounds.
  2. I don’t make an anonymous access, it’s named.

You have

args : access C.Strings.chars_ptr_array

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.

When I wrote that it was early, just got up and forgot that I had that anon access, I generally don’t use them in hand written bindings.

Also that “Args” function is generated by GCC, so trying not to modify anything, because it’ll need to be modified again every time it’s generated.

Thanks for the info. I need to read more about this as it’s complex af.