Version 1.9 of ais/ai-00177.txt

Unformatted version of ais/ai-00177.txt version 1.9
Other versions for file ais/ai-00177.txt

!standard B.3.1 (40)          99-09-15 AI95-00177/04
!class binding interpretation 97-03-19
!status Corrigendum 2000 99-05-27
!status WG9 Approved 97-11-14
!status ARG Approved (letter ballot: 9-0-2) 97-10-27
!status ARG Approved (subject to letter ballot) 4-0-6 97-04-11
!status work item 97-03-19
!status received 97-03-19
!priority High
!difficulty Medium
!qualifier Error
!subject Interfaces.C.Strings.Value with Length returning String
!summary
A call to the following function declared in Interfaces.C.Strings:
function Value (Item : in chars_ptr; Length : in size_t) return String;
is equivalent to:
<<Start_Example>> To_Ada( Value(Item, Length) & nul, Trim_Nul => True)
!question
The definition of the function Interfaces.C.Strings.Value which takes a chars_ptr and a length, and returning a String, seems wrong. As defined, it raises Terminator_Error anytime the null character is not found before hitting the specified length. This is because of the definition of To_Ada with Trim_Null => True.
Validation test cxb3011 in suite 2.1 seems to presume that no Terminator_Error should be raised when the input chars_ptr does not have a null within the specified length.
What is the intent?
!recommendation
(See Summary.)
!wording
Amend B.3.1(40) to say:
39 function Value (Item : in chars_ptr; Length : in size_t)
return String;
40 Equivalent to To_Ada( Value(Item, Length) & nul, Trim_Nul => True).
!discussion
B.3.1(40) says:
Equivalent to To_Ada(Value(Item, Length), Trim_Nul=>True).
However, this is incorrect. It makes no sense to trim the nul by default, and then complain about a missing nul.
!corrigendum B.03.01(40)
Replace the paragraph:
Equivalent to To_Ada(Value(Item, Length), Trim_Nul=>True).
by:
Equivalent to To_Ada(Value(Item, Length) & nul, Trim_Nul => True).
!ACATS test
This AI was prompted by the behavior of ACATS test CXB3011, and is adaquately tested by that test.
!appendix

!section B.3.1(40)
!subject Interfaces.C.Strings.Value with len returning String
!reference RM95 B.3.1(40)
!from Tucker Taft 1997-01-03
!reference 1997-15695.a Tucker Taft 1997-1-3>>
!discussion

The definition of the function Interfaces.C.Strings.Value which
takes a chars_ptr and a length, and returning a String, seems
wrong.  As defined, it raises Terminator_Error anytime the
null character is not found before hitting the specified length.
This is because of the definition of To_Ada with Trim_Null => True.

ACVC cxb3011.a in suite 2.1 seems to presume that no Terminator_Error
should be raised when the input chars_ptr does not have a null within
the specified length.

What is the intent?
-Tuck


****************************************************************

!section B.3.1(40)
!subject Interfaces.C.Strings.Value with len returning String
!reference RM95 B.3.1(40)
!reference 1997-15695.a
!from Ben Brosgol 1997-01-03
!reference 97-15696.a Ben Brosgol  97-1-3>>
!discussion

Tucker writes:

> The definition of the function Interfaces.C.Strings.Value which
> takes a chars_ptr and a length, and returning a String, seems
> wrong.  As defined, it raises Terminator_Error anytime the
> nul character is not found before hitting the specified length.
> This is because of the definition of To_Ada with Trim_Nul => True.

> ACVC cxb3011.a in suite 2.1 seems to presume that no Terminator_Error
> should be raised when the input chars_ptr does not have a nul within
> the specified length.

> What is the intent?
> -Tuck

The effect does look anomalous.  Here's an example:

declare
  x    : aliased char_array := "abcdefg" & nul;
  p    : chars_ptr          := To_Chars_Ptr( x'Unchecked_Access );

  ca_1 : char_array := Value( p );      -- "abcdefg" & nul
  ca_2 : char_array := Value( p, 10 );  -- "abcdefg" & nul
  ca_3 : char_array := Value( p, 5 );   -- "abcde"

  s_1  : String := Value( p );          -- "abcdefg"
  s_2  : String := Value( p, 10 );      -- "abcdefg"
  s_3  : String := Value( p, 5 );       -- ??
begin
  ...
end;

Intuitively, the value assigned to s_3 should just be "abcde", the first
5 characters of the string that p designates.  However, as Tucker points
out, the "equivalence" given in [RM95 B.3.1(40)] leads to the propagation
of Terminator_Error.  This looks like a bug in the RM.  Note that changing
the Trim_Nul parameter to False does not work, since then Value(p, 10) would
yield a nul-terminated String.  The following is probably the simplest
correction to B.3.1(40):

   Equivalent to To_Ada( Value(Item, Length) & nul, Trim_Nul => True).

The inner call Value(Item, Length) produces a char_array that might or
might not be nul-terminated; by concatenating nul we ensure nul
termination.  Thus the To_Ada call will not propagate Terminator_Error.

I suppose that an alternative would have been to define the semantics
differently for the Value(Item, Length) function that delivers a
char_array, in particular to specify that the result is always
nul-terminated.  This might have been feasible 2 years ago but
would not be appropriate now.

By the way, there are some other subtle problems with the equivalences
used to provide the semantics of the operations.  For example,
B.3.1(36), which defines the effect of Value(Item, Length) where
Item is a char_array, refers to Value(Item).  However, if Item does
not point to a nul-terminated String then evaluation of this function
is an erroneous execution which could therefore deliver something
strange like a null array which would then be the result of
invoking Value(Item, Length).  Maybe this will be fixed in Ada '0Y :-)

Ben Brosgol
brosgol@aonix.com




****************************************************************

!section B.3.1(40)
!subject Interfaces.C.Strings.Value with len returning String
!reference RM95 B.3.1(40)
!reference 97-15695.a Tucker Taft 1997-1-3
!reference 97-15696.a Ben Brosgol 97-1-3
!reference 97-15697.a Robert I. Eachus 97-1-6>>
!discussion

   Damn!  I thought that we had learned enough about the tribulations
of equivalence in Ada 83 to avoid things like this...

   Okay, two cases B 3.1(35 & 39):

function Value (Item: in chars_ptr; Length: in size_t) return char_array;

   In this case any implementation can always return the "right"
answer, since there may be a (implementation specific or just through
happenstance) nul downstream from the start of the string.  I think it
would be a good idea to create an AI that says that implementations
shouldn't look beyond Length characters.  In other words, a call
should not be erroneous if Item is nul terminated or at least Length
long.

function Value (Item: in chars_ptr; Length: in size_t) return String;

   This is the nasty case.  There are lots of innocent conversions
that will get clobbered by the definition as written, and it is
useless for the intended purpose.  Correcting Trim_Nul to false is not
right, the 'expected' semantics obviously are:

     To_Ada(Value(Item,Length) & nul, Trim_Nul => True)

   I think we should mandate that view.  This one may require debate,
but I think the first case needs to be corrected in any case.


                                        Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...


****************************************************************

!section B.3.1(40)
!subject Interfaces.C.Strings.Value with len returning String
!reference RM95 B.3.1(40)
!reference 1997-15695.a
!reference 97-15696.a
!from Dan Lehman 1997-01-06
!reference 97-15699.a Dan Lehman 97-1-7>>
!discussion

    [ps:  Note that Tuck's & Ben's posts carry slightly different formats
          for the "reference as" information--to wit:
**junk to defeat auto-sys** !reference 1997-15695.a Tucker Taft 1997-1-3>>
**junk to defeat auto-sys** !reference 97-15696.a Ben Brosgol  97-1-3>>
      (and Eachus's also comes in like Brosgol's--' 97 ' vs. '1997')
    ]


> The following is probably the simplest
> correction to B.3.1(40):
>
>    Equivalent to To_Ada( Value(Item, Length) & nul, Trim_Nul => True).
>
> The inner call Value(Item, Length) produces a char_array that might or
> might not be nul-terminated; by concatenating nul we ensure nul termination.

But, at least at the *intuitive* level, it seems silly to append nul when
one is asking precisely to Trim_Nul!  (-;

> Thus the To_Ada call will not propagate Terminator_Error.

What is the point of raising Terminator_Error, anyway?  Note that T_E is
NOT raised when Trim_Nul=False, yet one might think that THEN is precisely
when one would want it raised.
 (-;  intuitively:  "Don't trim the nul.  ... WHAT, there ISN'T a nul?!
                     Whoa, Nully!")

Is it the purpose of To_Ada to sort of type-check for correct C-string
formation (i.e. having nul termination), but only when Trim_Nul=True!?
Why not let such nul-checking be done explicitly by the program with
Is_Nul_Terminated functions, and leave the conversions to give the
with/without-nul results without exception?

---Dan
------- *



****************************************************************

!section B.3.1(40)
!subject Interfaces.C.Strings.Value with len returning String
!reference RM95 B.3.1(40)
!reference 97-15695.a Tucker Taft 1997-1-3
!reference 97-15696.a Ben Brosgol 97-1-3
!reference 97-15703.a Ben Brosgol  97-1-8>>
!discussion

Here is my response to Dan Lehman's questions/comments:

BB> The following is probably the simplest
BB> correction to B.3.1(40):
BB>
BB>    Equivalent to To_Ada( Value(Item, Length) & nul, Trim_Nul => True).
BB>
BB> The inner call Value(Item, Length) produces a char_array that might or
BB> might not be nul-terminated; by concatenating nul we ensure nul termination.

DL> But, at least at the *intuitive* level, it seems silly to append nul when
DL> one is asking precisely to Trim_Nul!  (-;

An alternative and perhaps more intuitive wording would be as follows:

"If Item = Null_Ptr then Value propagates Dereference_Error.  Otherwise
 the result String has lower bound 1 and comprises the elements obtained
 by applying To_Ada to each char in the array referenced by Item, stopping
 either immediately before the first occurrence of nul or immediately
 after Length chars, whichever happens earlier."

BB> Thus the To_Ada call will not propagate Terminator_Error.

DL> What is the point of raising Terminator_Error, anyway?  Note that T_E is
DL> NOT raised when Trim_Nul=False, yet one might think that THEN is
DL> precisely when one would want it raised.
DL>  (-;  intuitively:  "Don't trim the nul.  ... WHAT, there ISN'T a nul?!
DL>                     Whoa, Nully!")
DL>
DL> Is it the purpose of To_Ada to sort of type-check for correct C-string
DL> formation (i.e. having nul termination), but only when Trim_Nul=True!?
DL>  Why not let such nul-checking be done explicitly by the program with
DL> Is_Nul_Terminated functions, and leave the conversions to give the
DL> with/without-nul results without exception?

Some char_array values will be nul-terminated, whereas others might
either not contain a nul, or contain a nul as an interior char.
So when you convert these to Ada strings you have to indicate your
intent.  That is the point behind the Trim_Nul parameter for
To_Ada.  If you expect the C char_array to be nul terminated, and you
want to strip the nul when you convert it to Ada, then set
Trim_Nul to true.  In such a case the absence of a nul in the C
char_array is an error, so Terminator_Error is propagated.
If a nul in the char_array is not to be trimmed, or if there
might not be a nul there, then set Trim_Nul to False.  In this
case the absence of nul is not an error.  These semantics
seem pretty intuitive to me.  It would be inefficient to require
the program to check for nul termination explicitly with
a call on Is_Nul_Terminated before doing the conversion since
the same string scan is done in the termination check and the
conversion.

-Ben Brosgol
brosgol@aonix.com




****************************************************************

!section B.3.1(40)
!subject Interfaces.C.Strings.Value with len returning String
!reference RM95 B.3.1(40)
!reference 1997-15695.a
!reference 97-15696.a
!reference 97-15703.a
!from Dan Lehman 1997-01-08
!reference 97-15704.a Dan Lehman 97-1-8>>
!discussion


Well, I am ready to become *informed*, but on face value I'd
rearrange Ben's words for quite different semantics--as:

| If you expect the C char_array to be nul terminated [but it is NOT],
| in such a case the absence of a nul in the C char_array is an error,
| so Terminator_Error is propagated.

  [regardless of what was to be done with the nul by To_Ada!]

I.e., I don't see (but others can enlighten me) that, for only one of
these cases, the absence of a nul terminator is (more likely) an error:

   If you want to strip the nul when you convert it to Ada,
   then set Trim_Nul to true.

   If a nul in the char_array is not to be trimmed,
   then set Trim_Nul to False.

I'm suggesting/asking that To_Ada with Trim_Nul=True has a main function
of providing nul-free strings, not of checking char_arrays for nul.  In
this light, raising T_E seems unwanted.  (And, in this regard, To_Ada with
Trim_Nul=False *might* be seen to have a main function of yielding strings
WITH nul--e.g., if the Ada code is building a table of strings intended
to be later exported/shared with a C program (so needing nul termination).)

Terminator_Error is only raised by To_Ada (in, I ask, one dubious case)
and To_Chars_Ptr (when nul_check=true--a non-dubious case! :-).

---Dan
------- *



****************************************************************

!section B.3.1(40)
!subject Interfaces.C.Strings.Value with len returning String
!reference RM95 B.3.1(40)
!reference RM95 B.3(51)
!reference 1997-15695.a
!reference 97-15696.a
!reference 97-15703.a
!reference 97-15704.a
!from Ben Brosgol 1997-01-09
!reference 97-15705.a Ben Brosgol  97-1-9>>
!discussion

Dan says:

> I'm suggesting/asking that To_Ada with Trim_Nul=True has a main function
> of providing nul-free strings, not of checking char_arrays for nul.  In
> this light, raising T_E seems unwanted.  (And, in this regard, To_Ada with
> Trim_Nul=False *might* be seen to have a main function of yielding strings
> WITH nul--e.g., if the Ada code is building a table of strings intended
> to be later exported/shared with a C program (so needing nul termination).)

Well it's anyone's prerogative to ask for new semantics, but (aside from
the bug in B.3.1(40)) I don't see the rules as sufficiently broken to
warrant changes.  The model as it stands is consistent and corresponds
to common situations in practice.  If you're dealing with strings
constructed by a C program, then you know whether they will be nul
terminated or not.  It's unusual that you would have a char_array set by
a C function and not know whether nul is a terminator or just another
char.  It's more than unusual: without further information you'd have no
idea how to process the data.  For example imagine that the char_array
contains the value "abc" & nul & "de".  Is this to be interpreted as a
"abc" followed by a nul terminator, or as a 6-element array containing
nul between 'c' and 'd'?  The Ada program needs to know, and the
Trim_Nul parameter to To_Ada expresses its intent.  If the Ada program
expects the C data to be nul-terminated, then the absence of nul is an
error and an exception is appropriate.

-Ben Brosgol
brosgol@aonix.com

****************************************************************

Questions? Ask the ACAA Technical Agent