!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: <> 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) @drepl @xindentTrue).> @dby @xindent 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 ****************************************************************