comp.lang.ada
 help / color / mirror / Atom feed
* Calling Ada Procedure from C - how to handle a char ptr/array
@ 2011-11-18 19:40 awdorrin
  2011-11-18 19:56 ` Jeffrey Carter
  2011-11-19  2:16 ` Adam Beneschan
  0 siblings, 2 replies; 10+ messages in thread
From: awdorrin @ 2011-11-18 19:40 UTC (permalink / raw)


What would be the recommended way to pass a C char pointer or char
array into an Ada function or procedure that will be called from a C
program?

After reading through the Interfaces.C.* packages, I'm assuming I
could do something like this:

In my Ada procedure:

procedure Get_Text( s : out Interfaces.C.char_array) is
  Label : String(1..8)
begin
  Label(1..8) == "12345678";
  S := Interfaces.C.To_C(Label);
 end;

with a C program that does the following:

int main() {
  char temp[8+1];
  memset( temp, '\0', 9);

  adainit();
  Get_Text(temp);
  printf("Temp is: %s\n", temp);
  adafinal();
}

however when I compile the code I get the following warning:
  warning: type of argument "Get_Text.s" is unconstrained array
  warning: foreign caller must pass bounds explicitly

Then when I run, I get an error from the following line of code: S :=
Interfaces.C.To_C(Label);
"raised CONSTRAINT_ERROR : length check failed.

What length check?

Is there a better way to be doing this?

Thanks!
-Al



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-18 19:40 Calling Ada Procedure from C - how to handle a char ptr/array awdorrin
@ 2011-11-18 19:56 ` Jeffrey Carter
  2011-11-18 20:19   ` awdorrin
  2011-11-19  2:16 ` Adam Beneschan
  1 sibling, 1 reply; 10+ messages in thread
From: Jeffrey Carter @ 2011-11-18 19:56 UTC (permalink / raw)


On 11/18/2011 12:40 PM, awdorrin wrote:
> What would be the recommended way to pass a C char pointer or char
> array into an Ada function or procedure that will be called from a C
> program?
>
> After reading through the Interfaces.C.* packages, I'm assuming I
> could do something like this:
>
> In my Ada procedure:
>
> procedure Get_Text( s : out Interfaces.C.char_array) is
>    Label : String(1..8)
> begin
>    Label(1..8) == "12345678";

Someone's been writing C.

>    S := Interfaces.C.To_C(Label);
>   end;
 >
 > however when I compile the code I get the following warning:
 >    warning: type of argument "Get_Text.s" is unconstrained array
 >    warning: foreign caller must pass bounds explicitly

The problem here is that Ada doesn't know the bounds of S. C has to also pass 
that information.

 > Then when I run, I get an error from the following line of code: S :=
 > Interfaces.C.To_C(Label);
 > "raised CONSTRAINT_ERROR : length check failed.
 >
 > What length check?

An assignment to S must have a right-hand side that's the same length as S. I 
suspect you'll have to assign to a slice:

S (S'First .. S'First + Label'Length) := To_C (Label);

> Is there a better way to be doing this?

A function that returns a Char_Array might be better than an out parameter.

-- 
Jeff Carter
"Go and boil your bottoms."
Monty Python & the Holy Grail
01



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-18 19:56 ` Jeffrey Carter
@ 2011-11-18 20:19   ` awdorrin
  2011-11-18 20:41     ` awdorrin
  0 siblings, 1 reply; 10+ messages in thread
From: awdorrin @ 2011-11-18 20:19 UTC (permalink / raw)


Ok - so the warnings are just a reminder that both sides needs to know
how big the char_array will be, that makes sense.

I changed the Interface.C.To_C call to:

Interfaces.C_To(C(Label, s, Count, True);

and that worked without the Constraint error.

After looking at the source code in i-c.adb and reading your comments
I know understand why the Constraint Error was being raised.

Yea, I am much more a 'C Guy' than an 'Ada Guy' :)

Thanks!




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-18 20:19   ` awdorrin
@ 2011-11-18 20:41     ` awdorrin
  2011-11-18 21:38       ` Adam Beneschan
  2011-11-18 22:32       ` Georg Bauhaus
  0 siblings, 2 replies; 10+ messages in thread
From: awdorrin @ 2011-11-18 20:41 UTC (permalink / raw)


Tried reworking the code into a function that returns a char_array,
but got a STORAGE_ERROR with a stack overflow/erroneous memory access.

function Get_Label return Interfaces.C.Char_array is
  Label : String(1..8);
begin
  Label(1..8) := "12345678";

  return Interfaces.C.To_C(Label);
end;

From the source code for To_C(), it allocates the char array, so I'm
not sure why this error would be raised...



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-18 20:41     ` awdorrin
@ 2011-11-18 21:38       ` Adam Beneschan
  2011-11-18 22:32       ` Georg Bauhaus
  1 sibling, 0 replies; 10+ messages in thread
From: Adam Beneschan @ 2011-11-18 21:38 UTC (permalink / raw)


On Nov 18, 12:41 pm, awdorrin <awdor...@gmail.com> wrote:
> Tried reworking the code into a function that returns a char_array,
> but got a STORAGE_ERROR with a stack overflow/erroneous memory access.
>
> function Get_Label return Interfaces.C.Char_array is
>   Label : String(1..8);
> begin
>   Label(1..8) := "12345678";
>
>   return Interfaces.C.To_C(Label);
> end;
>
> From the source code for To_C(), it allocates the char array, so I'm
> not sure why this error would be raised...

I just tried a small test case (written entirely in Ada) that declared
this function just the way you wrote it, then called it.  I compiled
using GNAT.  It didn't raise anything.  Also, I don't see anything
wrong with the function.  So I suspect the problem isn't occurring in
this function; it's occurring somewhere else.

Are you calling Get_Label directly from C?  If so, how is it declared
in your C code?

I don't agree with Jeff's suggestion to use a function that returns a
Char_Array.  The reason is that Char_Array is defined as an
unconstrained array, and the language says that implementations don't
have to support applying the C convention to functions that return
unconstrained arrays.  When I try this (writing a source that defines
Get_Label as above, and applying Export(C) to it), GNAT gives a
warning "foreign convention function Get_Label should not return
unconstrained array".  Are you using GNAT?  If so, do you get the same
warning?  If so, that's a good indication that things aren't going to
work.  When an Ada function returns an unconstrained array, it has to
arrange to return the bounds as well as the array data, and how it
does that will vary from one implementation to another.  Our compiler,
for example, treats this kind of function as a procedure with an
implicit OUT parameter that points to a record that will contain the
data pointer and the bounds, which the caller will interpret as the
function result.  I don't know how GNAT handles it.  Regardless, it
will probably not be the way that a C program will expect, so you're
likely to get mismatched parameters or something else that could
easily lead to a bad memory access.

From the warning you described in your original post, and from my
experience, I don't think GNAT handles an *unconstrained* char_array
as an OUT parameter.  But there are a couple ways to get around this.
One is to declare your own constrained subtype:

   subtype constrained_char_array is Interfaces.C.Char_Array
(Interfaces.C.Size_T);

which declares a constrained array whose bounds are the entire range
of Size_T.  Of course, those won't be the actual bounds, but it will
make GNAT think those are the actual bounds so that it doesn't
complain about bounds not being passed in, and it doesn't try to check
indexes against some bogus bound and get a Constraint_Error.

   procedure Get_Text (s : out constrained_char_array) is
   ...
       s := Interfaces.C.To_C (Label);       -- WRONG

This is wrong, because it will try to assign to all of s from indexes
Size_T'First to Size_T'Last.  Remember, that's what it thinks the
bounds are.  (In your original attempt, it had no idea what the bounds
were, so assigning "s :=" couldn't have worked either.)  Since the C
code isn't going to pass any bound information to Get_Text, you have
to help it by telling it what index range you're setting:

       s(Size_T'First .. Size_T'First + Label'Length) :=
Interfaces.C.To_C (Label);

The length of the left-hand slice is Label'Length + 1, but that's what
you want because To_C will stick a NUL character on the end of the
string.  Of course, if the character array in your C code isn't large
enough to handle this result, you will be screwed, but that's par for
the course in C.  It might be better to have the C function pass in
the sizeof() of the array as an extra parameter, which is how it's
done in some of the Unix system calls.  Then the Ada code could check
to make sure that Label'Length + 1 <= [the Size parameter], or raise
Constraint_Error if not.

Another possibility is to use chars_ptr and Update in the
Interfaces.C.Strings package.  I'll let you look those up.

Hope this helps,

                               -- Adam



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-18 20:41     ` awdorrin
  2011-11-18 21:38       ` Adam Beneschan
@ 2011-11-18 22:32       ` Georg Bauhaus
  1 sibling, 0 replies; 10+ messages in thread
From: Georg Bauhaus @ 2011-11-18 22:32 UTC (permalink / raw)


On 18.11.11 21:41, awdorrin wrote:
> function Get_Label return Interfaces.C.Char_array is
>    Label : String(1..8);
> begin
>    Label(1..8) := "12345678";
>
>    return Interfaces.C.To_C(Label);
> end;

While only partly following GNAT's advice to not have Get_Label
return an unconstrained array (not following because this requires
defining a constrained subtype somewhere and I was lazy),
this works for me. It works with or without defining a constrained
Result array in Get_Label.

with Interfaces.C;
function Get_Label return Interfaces.C.Char_Array;
pragma Export (C, Get_Label, "get_label");

function Get_Label return Interfaces.C.Char_array is
   Label : String(1..8);
   Result : Interfaces.C.char_array(0..8);
begin
   Label(1..8) := "12345678";

   Result := Interfaces.C.To_C(Label);
   return Result;
end;

#include <stdio.h>

int main()
{
    extern char* get_label();
    extern void adainit(void);
    extern void adafinal(void);
  
    char* label;

    adainit();
    label = get_label();
    adafinal();

    fputs(label, stdout);
    return 0;
}


$ gnatmake -c -gnatwa -fstack-check -gnato -f get_label.adb
gcc -c -gnatwa -fstack-check -gnato get_label.adb
get_label.ads:2:10: warning: foreign convention function "Get_Label" should not return unconstrained array
$ cc -W -c with_label.c
$ gnatbind -n get_label.ali
$ gnatlink get_label.ali with_label.o -o test_get_label
$ ./test_get_label
12345678$



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-18 19:40 Calling Ada Procedure from C - how to handle a char ptr/array awdorrin
  2011-11-18 19:56 ` Jeffrey Carter
@ 2011-11-19  2:16 ` Adam Beneschan
  2011-11-21 16:33   ` awdorrin
  1 sibling, 1 reply; 10+ messages in thread
From: Adam Beneschan @ 2011-11-19  2:16 UTC (permalink / raw)


On Nov 18, 11:40 am, awdorrin <awdor...@gmail.com> wrote:
> What would be the recommended way to pass a C char pointer or char
> array into an Ada function or procedure that will be called from a C
> program?
>
> After reading through the Interfaces.C.* packages, I'm assuming I
> could do something like this:
>
> In my Ada procedure:
>
> procedure Get_Text( s : out Interfaces.C.char_array) is
>   Label : String(1..8)
> begin
>   Label(1..8) == "12345678";
>   S := Interfaces.C.To_C(Label);
>  end;

Since you seem to be pretty new to Ada, I think you may have some
misconceptions about how arrays work; so it might be useful to go over
some basics.

Array objects (this includes Strings) have bounds.  Once the bounds
are set for an object, they don't change.  For Example, Label has the
bounds 1..8 since you've declared it that way.  You can't make Label
shorter or longer by saying, for instance,

   Label := "123456";

This will get you a Constraint_Error.  S also has bounds.  Since S is
a parameter with an unconstrained array type, the bounds of S are
determined by whatever parameter gets passed when Get_Text is called.
(That's why it's a problem when it's called from C, because C code
won't know that it's supposed to give any bounds to Get_Text.)  But
whatever the bounds are of the parameter are, they won't change; you
can't change the bounds, or make S longer or shorter, by assigning to
it.  That means that in your statement:

   S := Interfaces.C.To_C (Label);

The right-hand side will be an array with 9 characters (since Label
has 8, and To_C appends a NUL character).  This statement will
therefore work only if S's bounds are such that S will have exactly 9
characters.  (I.e. if S's bounds are (1..9), (3..11), (100..108),
etc.)  Otherwise it will raise Constraint_Error.

By the way, that applies to OUT parameters just as well as IN and IN
OUT parameters.  For an unconstrained array, OUT means that the data
in the array will be set by the procedure.  But the *bounds* of the
array parameter are still input to the procedure, not output from it,
and the procedure can't change them.

If you need to set a portion of S, you need to use a slice on the
left:

  S (S'First .. S'First + Label'Length) := Interfaces.C.To_C (Label);

which takes advantage of the knowledge that To_C returns a string
whose length is Label'Length + 1.  Or there are operations in
Ada.Strings.Fixed to help with this:

  Ada.Strings.Fixed.Overwrite (S, S'First, Interfaces.C.To_C (Label));

Hope this helps,

                         -- Adam



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-19  2:16 ` Adam Beneschan
@ 2011-11-21 16:33   ` awdorrin
  2011-11-22 13:20     ` awdorrin
  0 siblings, 1 reply; 10+ messages in thread
From: awdorrin @ 2011-11-21 16:33 UTC (permalink / raw)


I've been side-tracked on another task this morning so I haven't had a
chance to investigate this further, but wanted to respond with a
'Thanks' for the suggestions and the related information.

My familiarity with Ada is limited - I understand the basics but when
interfacing with C, I have a tendency to get myself confused - since
it seems that a lot of the 'constraint' information that Ada uses is
packed in different locations depending upon the compiler. The
original programmers of this code made some assumptions that do
appears to be entirely compiler dependent and are not portable moving
to GNAT.

It would seem that modifying my approach to use the
Interfaces.C.strings and char_ptr would make more sense.

Thanks!




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-21 16:33   ` awdorrin
@ 2011-11-22 13:20     ` awdorrin
  2011-11-22 13:28       ` Simon Wright
  0 siblings, 1 reply; 10+ messages in thread
From: awdorrin @ 2011-11-22 13:20 UTC (permalink / raw)


Had a chance to review the Interfaces.C.Strings package and I think
that the Chars_Ptr will work out a lot better for what I need than
passing around char_array types.

The Interfaces.C.Strings package looks like it can replace the custom
code that was written back in the early 90s for this project, with
some very minor adaptations.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Calling Ada Procedure from C - how to handle a char ptr/array
  2011-11-22 13:20     ` awdorrin
@ 2011-11-22 13:28       ` Simon Wright
  0 siblings, 0 replies; 10+ messages in thread
From: Simon Wright @ 2011-11-22 13:28 UTC (permalink / raw)


awdorrin <awdorrin@gmail.com> writes:

> Had a chance to review the Interfaces.C.Strings package and I think
> that the Chars_Ptr will work out a lot better for what I need than
> passing around char_array types.
>
> The Interfaces.C.Strings package looks like it can replace the custom
> code that was written back in the early 90s for this project, with
> some very minor adaptations.

That's good. It seems likely that that's exactly why Interfaces.C.* were
written!



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2011-11-22 13:28 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-11-18 19:40 Calling Ada Procedure from C - how to handle a char ptr/array awdorrin
2011-11-18 19:56 ` Jeffrey Carter
2011-11-18 20:19   ` awdorrin
2011-11-18 20:41     ` awdorrin
2011-11-18 21:38       ` Adam Beneschan
2011-11-18 22:32       ` Georg Bauhaus
2011-11-19  2:16 ` Adam Beneschan
2011-11-21 16:33   ` awdorrin
2011-11-22 13:20     ` awdorrin
2011-11-22 13:28       ` Simon Wright

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox