comp.lang.ada
 help / color / mirror / Atom feed
* Trouble writing Ada callback from C
@ 2009-09-11  7:57 Jerry
  2009-09-11  8:24 ` Ludovic Brenta
  2009-09-12 18:40 ` Vadim Godunko
  0 siblings, 2 replies; 9+ messages in thread
From: Jerry @ 2009-09-11  7:57 UTC (permalink / raw)


I have a problem with writing an Ada procedure, used as a callback
from C, in which one of the arguments is an output char * (in the C
version). The actual length of the returned "string" isn't known at
calling time, even though the caller apparently allocates 40
characters (see below). However, the actual number of characters
returned by the C version is less than 40.

My problem is how to pass probably a char_array back out which is the
correct length, but which length is not known at the time the Ada
callback is called from C.

Here is a highly shortened version of one of many things that I have
tried. I have not compiled or run this shortened version, but I
believe that it contains the essence of the longer version:


procedure geolocation_labeler
   (label    : out char_array;
    a_length : size_t);
pragma Convention(C, geolocation_labeler);

-- Highly shortened version.
procedure geolocation_labeler
   (label    : out char_array;
    a_length : size_t)
is
    direction_label : Unbounded_String;
begin
    Put_Line("a_length = " & a_length'img); -- for testing
    direction_label := To_Unbounded_String(" N"); -- not always length
2
    label := To_C(To_String(direction_label), False);
end geolocation_labeler;


Of course, this fails with:
raised CONSTRAINT_ERROR : blabla.adb:xxx length check failed

Note that the Put_Line always prints 40, passed by the C caller.



Here is a highly shortened version of the C callback. Note that PLINT
is just an integer.


/* Highly shortened version.*/
void
geolocation_labeler(char *label, PLINT length) {
    const char *direction_label;

    direction_label = " N";
    snprintf(label, length, "%s", direction_label);
    printf(label); /* for testing */
}


The printf(label) returns exactly <space>N (in this case) without \n.
In particular, it is only 2 characters, not 40. (Presumably, length
has a value of 40 here as well, although I have not tested that
assumption.)


I have tried things such as changing the type of label in the argument
list then declaring e.g.

label_as_char_array : aliased char_array := (0 .. whatever => ' ');
label_as_char_array_ptr : char_array_access;

then building the string in label_as_char_array, then doing

label_as_char_array_ptr := label_as_char_array'Unchecked_Access;

and then returning label_as_char_array_ptr. That also didn't work.


So how do I mimic the char * behavior of C when the length of the
output array is not known at call time?

Jerry



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

* Re: Trouble writing Ada callback from C
  2009-09-11  7:57 Trouble writing Ada callback from C Jerry
@ 2009-09-11  8:24 ` Ludovic Brenta
  2009-09-11 20:38   ` sjw
  2009-09-12  5:28   ` Jerry
  2009-09-12 18:40 ` Vadim Godunko
  1 sibling, 2 replies; 9+ messages in thread
From: Ludovic Brenta @ 2009-09-11  8:24 UTC (permalink / raw)


On Sep 11, 9:57 am, Jerry <lancebo...@qwest.net> wrote:
> I have a problem with writing an Ada procedure, used as a callback
> from C, in which one of the arguments is an output char * (in the C
> version). The actual length of the returned "string" isn't known at
> calling time, even though the caller apparently allocates 40
> characters (see below). However, the actual number of characters
> returned by the C version is less than 40.
>
> My problem is how to pass probably a char_array back out which is the
> correct length, but which length is not known at the time the Ada
> callback is called from C.
>
> Here is a highly shortened version of one of many things that I have
> tried. I have not compiled or run this shortened version, but I
> believe that it contains the essence of the longer version:
>
> procedure geolocation_labeler
>    (label    : out char_array;
>     a_length : size_t);
> pragma Convention(C, geolocation_labeler);
>
> -- Highly shortened version.
> procedure geolocation_labeler
>    (label    : out char_array;
>     a_length : size_t)
> is
>     direction_label : Unbounded_String;
> begin
>     Put_Line("a_length = " & a_length'img); -- for testing
>     direction_label := To_Unbounded_String(" N"); -- not always length
> 2
>     label := To_C(To_String(direction_label), False);
> end geolocation_labeler;
>
> Of course, this fails with:
> raised CONSTRAINT_ERROR : blabla.adb:xxx length check failed
>
> Note that the Put_Line always prints 40, passed by the C caller.
>
> Here is a highly shortened version of the C callback. Note that PLINT
> is just an integer.
>
> /* Highly shortened version.*/
> void
> geolocation_labeler(char *label, PLINT length) {
>     const char *direction_label;
>
>     direction_label = " N";
>     snprintf(label, length, "%s", direction_label);
>     printf(label); /* for testing */
>
> }
>
> The printf(label) returns exactly <space>N (in this case) without \n.
> In particular, it is only 2 characters, not 40. (Presumably, length
> has a value of 40 here as well, although I have not tested that
> assumption.)
>
> I have tried things such as changing the type of label in the argument
> list then declaring e.g.
>
> label_as_char_array : aliased char_array := (0 .. whatever => ' ');
> label_as_char_array_ptr : char_array_access;
>
> then building the string in label_as_char_array, then doing
>
> label_as_char_array_ptr := label_as_char_array'Unchecked_Access;
>
> and then returning label_as_char_array_ptr. That also didn't work.
>
> So how do I mimic the char * behavior of C when the length of the
> output array is not known at call time?

I take it you mean "not known at compile time" because it is indeed
"known at call time".

C doesn't have proper strings; it has pointers to null-terminated
arrays of characters, which do not carry bounds information. So, the
specification of your callback is not "return a string in an array"
but "write N characters starting where label points, where N+1 <
a_length, followed by a null character (ASCII.NUL)".  Here is a
possible implementation (not compiled or tested either):

with Interfaces.C.Strings; use Interfaces.C.Strings;
procedure geolocation_labeler (label : in chars_ptr; length : in
size_t) is
   Result : constant String := " N"; -- or whatever
begin
   Update (Item => label, Offset => 0, Str => To_C (Result), Check =>
False);
   -- See AARM B.3.1(43, 50.a/2)
exception
   when others =>
      -- Important: callbacks must handle all exceptions because the C
layer
      -- calling us cannot propagate or handle them.
      null; -- or report it.
end geolocation_labeler;

HTH

--
Ludovic Brenta.



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

* Re: Trouble writing Ada callback from C
  2009-09-11  8:24 ` Ludovic Brenta
@ 2009-09-11 20:38   ` sjw
  2009-09-12  5:28   ` Jerry
  1 sibling, 0 replies; 9+ messages in thread
From: sjw @ 2009-09-11 20:38 UTC (permalink / raw)


On Sep 11, 9:24 am, Ludovic Brenta <ludo...@ludovic-brenta.org> wrote:

> procedure geolocation_labeler (label : in chars_ptr; length : in
> size_t) is
>    Result : constant String := " N"; -- or whatever
> begin
>    Update (Item => label, Offset => 0, Str => To_C (Result), Check =>
> False);

Check that Result'Length < length (have to leave room for the
terminating nul)



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

* Re: Trouble writing Ada callback from C
  2009-09-11  8:24 ` Ludovic Brenta
  2009-09-11 20:38   ` sjw
@ 2009-09-12  5:28   ` Jerry
  2009-09-12  5:46     ` sjw
  2009-09-12  8:44     ` Georg Bauhaus
  1 sibling, 2 replies; 9+ messages in thread
From: Jerry @ 2009-09-12  5:28 UTC (permalink / raw)


On Sep 11, 1:24 am, Ludovic Brenta <ludo...@ludovic-brenta.org> wrote:
>
> I take it you mean "not known at compile time" because it is indeed
> "known at call time".
>
> C doesn't have proper strings; it has pointers to null-terminated
> arrays of characters, which do not carry bounds information. So, the
> specification of your callback is not "return a string in an array"
> but "write N characters starting where label points, where N+1 <
> a_length, followed by a null character (ASCII.NUL)".  Here is a
> possible implementation (not compiled or tested either):
>
> with Interfaces.C.Strings; use Interfaces.C.Strings;
> procedure geolocation_labeler (label : in chars_ptr; length : in
> size_t) is
>    Result : constant String := " N"; -- or whatever
> begin
>    Update (Item => label, Offset => 0, Str => To_C (Result), Check =>
> False);
>    -- See AARM B.3.1(43, 50.a/2)
> exception
>    when others =>
>       -- Important: callbacks must handle all exceptions because the C
> layer
>       -- calling us cannot propagate or handle them.
>       null; -- or report it.
> end geolocation_labeler;
>
> HTH
>
> --
> Ludovic Brenta.

Thanks, Ludovic.

Unfortunately, the C overlords are giving me a string of length 0--the
first character is the null character. So Update quits with

raised INTERFACES.C.STRINGS.UPDATE_ERROR

as is expected since Update can't write before the null character.

I'm now (desperately) studying the GNAT code in i-cstrin.adb and
attempting to make something like its Poke command, and then using it
in a modified Update which I'm calling Unsafe_Update which is just
like Update but with these lines deleted:

      if Check and then Offset + Chars'Length  > Strlen (Item) then
         raise Update_Error;
      end if;

I'm getting several errors which I'm trying to work through.

Jerry



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

* Re: Trouble writing Ada callback from C
  2009-09-12  5:28   ` Jerry
@ 2009-09-12  5:46     ` sjw
  2009-09-16 23:06       ` Jerry
  2009-09-12  8:44     ` Georg Bauhaus
  1 sibling, 1 reply; 9+ messages in thread
From: sjw @ 2009-09-12  5:46 UTC (permalink / raw)


On Sep 12, 6:28 am, Jerry <lancebo...@qwest.net> wrote:
> On Sep 11, 1:24 am, Ludovic Brenta <ludo...@ludovic-brenta.org> wrote:

> > begin
> >    Update (Item => label, Offset => 0, Str => To_C (Result), Check =>
> > False);

>       if Check and then Offset + Chars'Length  > Strlen (Item) then
>          raise Update_Error;
>       end if;

Are you sure you called Update with Check => False?



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

* Re: Trouble writing Ada callback from C
  2009-09-12  5:28   ` Jerry
  2009-09-12  5:46     ` sjw
@ 2009-09-12  8:44     ` Georg Bauhaus
  1 sibling, 0 replies; 9+ messages in thread
From: Georg Bauhaus @ 2009-09-12  8:44 UTC (permalink / raw)


Jerry wrote:

> Unfortunately, the C overlords are giving me a string of length 0--the
> first character is the null character. So Update quits with
> 
> raised INTERFACES.C.STRINGS.UPDATE_ERROR
> 
> as is expected since Update can't write before the null character.
> 
> I'm now (desperately) studying the GNAT code in i-cstrin.adb and
> attempting to make something like its Poke command, and then using it
> in a modified Update which I'm calling Unsafe_Update which is just
> like Update but with these lines deleted:
> 
>       if Check and then Offset + Chars'Length  > Strlen (Item) then
>          raise Update_Error;
>       end if;
> 
> I'm getting several errors which I'm trying to work through.

The following works---if I understand correctly what you
are trying to achieve.

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

extern void adainit(void);
extern void adafinal(void);
extern int geolocation_labeler(char*, size_t);

int main()
{
  char* buffer = calloc(2048, sizeof(char));

  adainit();

  assert(*(buffer + 112) == '\0');

  if (geolocation_labeler(buffer + 112, 40) >= 0)
    fputs(buffer + 112, stdout);
  else
    fputs("no label", stderr);

  adafinal();
  return 0;
}


with Interfaces.C.Strings; use Interfaces.C;

function Geolocation_Labeler
  (label : in Strings.chars_ptr; length : in size_t) return int;
pragma Export(C, Geolocation_Labeler, "geolocation_labeler");


function Geolocation_Labeler
  (label : in Strings.chars_ptr; length : in size_t) return int
is
   Result : constant String := " N"; -- or whatever
begin
   if Result'Length + 1 > Length then
      return -1;
   end if;

   Strings.Update (Item => label,
     Offset => 0,
     Chars => To_C (Result, Append_Nul => True),
     Check => False);
   return Result'Length;
exception
   when others =>
      return -2;
end geolocation_labeler;



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

* Re: Trouble writing Ada callback from C
  2009-09-11  7:57 Trouble writing Ada callback from C Jerry
  2009-09-11  8:24 ` Ludovic Brenta
@ 2009-09-12 18:40 ` Vadim Godunko
  2009-09-16 23:19   ` Jerry
  1 sibling, 1 reply; 9+ messages in thread
From: Vadim Godunko @ 2009-09-12 18:40 UTC (permalink / raw)


On Sep 11, 11:57 am, Jerry <lancebo...@qwest.net> wrote:
> I have a problem with writing an Ada procedure, used as a callback
> from C, in which one of the arguments is an output char * (in the C
> version). The actual length of the returned "string" isn't known at
> calling time, even though the caller apparently allocates 40
> characters (see below). However, the actual number of characters
> returned by the C version is less than 40.
>
> My problem is how to pass probably a char_array back out which is the
> correct length, but which length is not known at the time the Ada
> callback is called from C.
>

subtype constrained_char_array is Interfaces.C.char_array (1 .. 40);

procedure Y (X : out constrained_char_array);
pragma Convention (C, Y);



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

* Re: Trouble writing Ada callback from C
  2009-09-12  5:46     ` sjw
@ 2009-09-16 23:06       ` Jerry
  0 siblings, 0 replies; 9+ messages in thread
From: Jerry @ 2009-09-16 23:06 UTC (permalink / raw)


On Sep 11, 10:46 pm, sjw <simon.j.wri...@mac.com> wrote:
> On Sep 12, 6:28 am, Jerry <lancebo...@qwest.net> wrote:
>
> > On Sep 11, 1:24 am, Ludovic Brenta <ludo...@ludovic-brenta.org> wrote:
> > > begin
> > >    Update (Item => label, Offset => 0, Str => To_C (Result), Check =>
> > > False);
> >       if Check and then Offset + Chars'Length  > Strlen (Item) then
> >          raise Update_Error;
> >       end if;
>
> Are you sure you called Update with Check => False?

I called it with Check => True. However, when I tried it with Check =>
False, I got the same error message. Interesting, Norman Cohen's book
says I should get a different error message with Check => False. I
didn't check ARM about the message, however.
Jerry



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

* Re: Trouble writing Ada callback from C
  2009-09-12 18:40 ` Vadim Godunko
@ 2009-09-16 23:19   ` Jerry
  0 siblings, 0 replies; 9+ messages in thread
From: Jerry @ 2009-09-16 23:19 UTC (permalink / raw)


On Sep 12, 11:40 am, Vadim Godunko <vgodu...@gmail.com> wrote:
> On Sep 11, 11:57 am, Jerry <lancebo...@qwest.net> wrote:
>
> > I have a problem with writing an Ada procedure, used as a callback
> > from C, in which one of the arguments is an output char * (in the C
> > version). The actual length of the returned "string" isn't known at
> > calling time, even though the caller apparently allocates 40
> > characters (see below). However, the actual number of characters
> > returned by the C version is less than 40.
>
> > My problem is how to pass probably a char_array back out which is the
> > correct length, but which length is not known at the time the Ada
> > callback is called from C.
>
> subtype constrained_char_array is Interfaces.C.char_array (1 .. 40);
>
> procedure Y (X : out constrained_char_array);
> pragma Convention (C, Y);

Sorry for being slow to respond--I had to step away from this project
for a few days.

This works--thanks! After seeing this suggestion, I felt a little
embarrassed because it's so simple, and in hindsight, obvious.

Also, I realized that part of the problem was that to match the C
results, I had to insert my string into the left of the allocated 41
characters, then place a null character, then fill the rest with
spaces, like this:

-- Declared elsewhere is this, with Label_String_Length set to 40:
subtype Label_String_Type is
    Interfaces.C.char_array (0 .. size_t(Label_String_Length));

-- Then:
function Unbounded_To_Weird_C
   (Item     : Unbounded_String;
    C_Length : size_t)
return Label_String_Type is
begin
   return To_C(To_String(Item & ASCII.nul &
        (Label_String_Length - Length(Item)) * " "), False);
end Unbounded_To_Weird_C;


Thanks, everyone, for the help.
Jerry



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

end of thread, other threads:[~2009-09-16 23:19 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-11  7:57 Trouble writing Ada callback from C Jerry
2009-09-11  8:24 ` Ludovic Brenta
2009-09-11 20:38   ` sjw
2009-09-12  5:28   ` Jerry
2009-09-12  5:46     ` sjw
2009-09-16 23:06       ` Jerry
2009-09-12  8:44     ` Georg Bauhaus
2009-09-12 18:40 ` Vadim Godunko
2009-09-16 23:19   ` Jerry

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