Version 1.1 of ai12s/ai12-0384-2.txt
!standard 4.10(0) 20-10-14 AI12-0384-2/01
!class Amendment 20-10-14
!status work item 20-10-14
!status received 20-10-09
!priority Low
!difficulty Medium
!subject Fixups for Put_Image and Text_Buffers
!summary
Add operations to associate with a Text_Buffer an adjustable level of
indentation. Eliminate the End_Of_Line function. Add operations for
putting and getting the content of the Text_Buffer as a UTF_8 String or
as a UTF_16 Wide_String. Provide functions Get, Wide_Get, and
Wide_Wide_Get rather than procedures for retrieving the whole buffer to
match what is needed by '*_Image, with implementation-defined
substitutions in Get and Wide_Get for unrepresentable characters, as
appropriate for 'Image and 'Wide_Image (intended to match Ada 2012
implementation-defined behavior for things like Enum'Image and
Wide_Character'Image).
Move the various Get routines to the child packages, as they are not
needed in the root package. Remove the requirement to raise
Constraint_Error on buffer overflow in the Bounded child, and instead
provide a query function to determine whether the buffer overflowed.
Allow Put_Image on an Unchecked Union. Give implementors a bit more
flexibility about whether to support Put_Image on types on which
Text_Buffers themselves depend. Clarify that T'Put_Image on a private
type breaks privacy, and displays the same thing that the full type
would display.
!problem
As part of implementing the Put_Image attribute (AI12-0020-1) and the
Text_Buffers package (AI12-0340-1), a certain number of issues were
identified with the proposed features. In particular:
(1) Defining the Put_Image for a composite type is likely to result in
multiple lines of output. It seems highly desirable that such output is
reasonably indented, to reflect the nesting of components and
subcomponents. As currently defined the Put_Image attribute takes only
the Text_Buffer and the value whose image is to be produced. If we want
to provide some level of indenting, we need to incorporate into the
Text_Buffer some notion of a current level of indentation.
(2) The Get and Wide_Get procedures currently produce
implementation-defined results when the characters are not representable
directly as Character or Wide_Character, respectively. But what we really
need are functions to be used by 'Image and 'Wide_Image, because the
implementation-defined substitutions might involve multiple characters
replacing what was a single original character, at least in some
implementations. There also seems no need for these Get functions in the
Root_Buffer_Type, so we move them to the child packages Unbounded and Bounded.
(3) It would also be useful if UTF_8 and UTF_16 could be used for
filling and emptying Text_Buffers, since we anticipate that many
implementations will use UTF_8 or UTF_16 internally for representing
identifiers in their symbol tables, and perhaps also for the characters
in a Text_Buffer. This can also make the process "loss-less" while
permitting efficient internal representation.
(4) There seems no particular advantage in having the Put procedures
of the Bounded child package raise Constraint_Error on buffer overflow.
A more common use-case is to want the buffer to silently stop storing
characters, but keep a flag to indicate that an overflow occurred,
so perhaps an ellipsis ("...") could be displayed at the end.
Any Put_Image routine could check the flag should it so choose, and
any user of a Bounded buffer could raise an exception after any call
on Put_Image that produced an overflow situation.
(5) Providing a "peek-ahead" capability via End_Of_Line for a newline
character sequence is not particularly useful unless characters are
being removed from the buffer one character at a time. And even then,
other "peek-ahead" would also be needed (e.g. for white space, commas,
brackets, etc.) to do any sort of complex parsing as might be required
by a general 'Value implementation.
(6) An AARM note says that Put_Image on an Unchecked_Union will raise
Program_Error. It might be nice if some other behavior were permitted.
!proposal
(1) We propose to add indentation to the root interface, because it is
not easy to add indentation in an extension, given that the various
Put_Image routines are only passed Root_Buffer_Type'Class. We propose a
Current_Indent function, and Increase/Decrease_Indent procedures, as
primitives, which determine how many spaces are inserted at the
beginning of the buffer, and at the beginning of each nonempty line.
(2) We replace the the Get, Wide_Get, and Wide_Wide_Get procedures with
Get, Wide_Get, and Wide_Wide_Get functions, which (in the case of Get
and Wide_Get) provide backward-compatible implementation-defined
substitutions for unrepresentable characters, as appropriate for 'Image
and 'Wide_Image.
(3) We add Get/Put_UTF_8 and Wide_Get/Put_UTF_16 operations.
(4) We provide a Buffer_Overflowed function for checking the state of a
Bounded buffer.
(5) We remove the End_Of_Line function. We leave for future
standardization sufficient peek-ahead functions to support 'Value or
similar lexing and parsing uses of Text_Buffers.
(6) We give an implementation permission for the default Put_Image for
an unchecked union to produce a recognizable string rather than raise
Program_Error.
!wording
Modify 4.10 (7/5):
where the Wide_Wide_String value written out to the [stream]{text
buffer} is defined as follows:
Modify AARM 4.10(13.a/5):
In general, the default implementation of T'Put_Image for a composite
type will involve some sequence of calls to [Wide_Wide_String'Write]
{Put and its Wide and Wide_Wide variants} and calls to the Put_Image
procedures of component types and, in the case of an array type, index
types. The [Wide_Wide_String'Write]{Put} calls may pass in either
literal values (e.g., "(", ")", "'(", " => ", or ", "), or other
things (such as component names for record values, task_id images for
tasks, or the Wide_Wide_Expanded_Name of the tag in the class-wide
case).
Modify 4.10(16/5):
For a class-wide type, the default implementation of T'Put_Image
generates an image based on qualified expression syntax.
[Wide_Wide_String'Write] {Wide_Wide_Put} is called with
Wide_Wide_Expanded_Name of Arg'Tag. Then S'Put_Image is called, where
S is the specific type identified by Arg'Tag.
Add after 4.10 (23/5):
Redundant[T'Put_Image is the same for both the partial
view and full view of T, if T has a partial view.]
AARM Proof: A type-related operational aspect is the same for the full
view and partial view of a type. See 13.1.
Modify 4.10 (25.2/5):
S'Wide_Wide_Image calls S'Put_Image passing Arg (which will typically
store a sequence of character values in a text buffer) and then
returns the result of retrieving the contents of that buffer with
{function} Wide_Wide_Get. The lower bound of the result is one.
Modify 4.10 (28/5):
S'Wide_Image calls S'Put_Image passing Arg (which will typically store
a sequence of character values in a text buffer) and then returns the
result of retrieving the contents of that buffer with {function}
Wide_Get. The lower bound of the result is one.
Modify 4.10 (31/5):
S'Image calls S'Put_Image passing Arg (which will typically store a
sequence of character values in a text buffer) and then returns the
result of retrieving the contents of that buffer with {function} Get.
The lower bound of the result is one.
Modify 4.10 (41/5):
* Additional spaces (Wide_Wide_Characters with position 32), and calls
to the New_Line operation of a text buffer, may be inserted to
improve readability of the generated image, with the spaces inserted
directly or via use of the Increase_ and Decrease_Indent procedures.
Add after 4.10 (41/5):
* For a string type, implementations may produce an image corresponding
to a string literal.
* For an unchecked union type, implementations may
raise Program_Error or produce some recognizable
image (such as "(UNCHECKED UNION)") that does not
require reading the discriminants.
Replace A.4.12 (3/5-25/5) with the following:
with Ada.Strings.UTF_Encoding.Wide_Wide_Strings;
package Ada.Strings.Text_Buffers
with Pure is
type Text_Buffer_Count is range 0 .. /implementation-defined/;
New_Line_Count : constant Text_Buffer_Count := /implementation-defined/;
type Root_Buffer_Type is abstract tagged limited private
with Default_Initial_Condition =>
Current_Indent (Root_Buffer_Type) = 0;
procedure Put (
Buffer : in out Root_Buffer_Type;
Item : in String) is abstract;
procedure Wide_Put (
Buffer : in out Root_Buffer_Type;
Item : in Wide_String) is abstract;
procedure Wide_Wide_Put (
Buffer : in out Root_Buffer_Type;
Item : in Wide_Wide_String) is abstract;
procedure Put_UTF_8 (
Buffer : in out Root_Buffer_Type;
Item : in UTF_Encoding.UTF_8_String) is abstract;
procedure Wide_Put_UTF_16 (
Buffer : in out Root_Buffer_Type;
Item : in UTF_Encoding.UTF_16_Wide_String) is abstract;
procedure New_Line (Buffer : in out Root_Buffer_Type)
is abstract;
Standard_Indent : constant Text_Buffer_Count := 3;
function Current_Indent
(Buffer : Root_Buffer_Type) return Text_Buffer_Count;
procedure Increase_Indent
(Buffer : in out Root_Buffer_Type;
Amount : in Text_Buffer_Count := Standard_Indent)
with Post'Class =>
Current_Indent (Buffer) = Current_Indent (Buffer)'Old + Amount;
procedure Decrease_Indent
(Buffer : in out Root_Buffer_Type;
Amount : in Text_Buffer_Count := Standard_Indent)
with Pre'Class =>
Current_Indent (Buffer) >= Amount
or else raise Constraint_Error,
Post'Class =>
Current_Indent (Buffer) =
Current_Indent (Buffer)'Old - Amount;
private
... --
end Ada.Strings.Text_Buffers;
package Ada.Strings.Text_Buffers.Unbounded
with Preelaborate, Nonblocking, Global => null is
type Buffer_Type is new Root_Buffer_Type with private;
function Get (
Buffer : in out Buffer_Type)
return String
with Post'Class =>
Get'Result'First = 1
and then Current_Indent (Buffer) = 0;
function Wide_Get (
Buffer : in out Buffer_Type)
return Wide_String
with Post'Class =>
Wide_Get'Result'First = 1
and then Current_Indent (Buffer) = 0;
function Wide_Wide_Get (
Buffer : in out Buffer_Type)
return Wide_Wide_String
with Post'Class =>
Wide_Wide_Get'Result'First = 1
and then Current_Indent (Buffer) = 0;
function Get_UTF_8 (
Buffer : in out Buffer_Type)
return UTF_Encoding.UTF_8_String
with Post'Class =>
Get_UTF_8'Result'First = 1
and then Current_Indent (Buffer) = 0;
function Wide_Get_UTF_16 (
Buffer : in out Buffer_Type)
return UTF_Encoding.UTF_16_Wide_String
with Post'Class =>
Wide_Get_UTF_16'Result'First = 1
and then Current_Indent (Buffer) = 0;
private
... --
... --
... --
end Ada.Strings.Text_Buffers.Unbounded;
package Ada.Strings.Text_Buffers.Bounded
with Pure is
type Buffer_Type (Max_Characters : Text_Buffer_Count)
is new Root_Buffer_Type with private
with Default_Initial_Condition =>
not Buffer_Overflowed (Buffer_Type);
function Buffer_Overflowed (Buffer : in Buffer_Type) return Boolean;
--
--
private
... --
... --
... --
end Ada.Strings.Text_Buffers.Bounded;
Modify A.4.12 (27/5):
New_Line stores New_Line_Count characters that represent a new line
into a text buffer. [End_of_Line returns True if the next characters
to be retrieved from the text buffer represent a new line.]
{Current_Indent returns the current indentation associated with the
buffer, with zero meaning there is no indentation in effect;
Increase_Indent and Decrease_Indent increase or decrease the
indentation associated with the buffer.}
Modify A.4.12 (28/5):
A call to Put, Wide_Put, [or] Wide_Wide_Put{, Put_UTF_8, or
Wide_Put_UTF_16} stores a sequence of characters into the text
buffer{, preceded by Current_Indent(Buffer) spaces
(Wide_Wide_Characters with position 32) if there is at least one
character in Item and it would have been the first character on the
current line}.
Modify A.4.12 (29/5):
A call to {function} Get, Wide_Get, [or] Wide_Wide_Get{, Get_UTF_8, or
Wide_Get_UTF_16} returns the same sequence of characters as was
present in the calls that stored the characters into the buffer{, if
representable}. For a call to Get, if any character in the sequence is
not defined in Character, the result is implementation defined.
Similarly, for a call to Wide_Get, if any character in the sequence is
not defined in Wide_Character, the result is implementation defined.
{As part of a call on any of the Get functions, the buffer is reset to
an empty state, with no stored characters.}
Add after A.4.12
In the case of a Buf of type Text_Buffers.Bounded.Buffer_Type,
Buffer_Overflowed (Buf) returns True if the various Put procedures
together have attempted to store more than Buf.Max_Characters into
Buf. If this function returns True, then the various Get functions
return a representation of only the first Buf.Max_Characters
characters that were stored in Buf.
Modify H.4(23.e/5):
Implementation Note: [Assuming the Implementation Advice is followed,
t]{T}his can be accomplished by using an object of the
Text_Buffers.Bounded.Buffer_Type with the maximum characters as
specified in the Max_Image_Length restriction{, with a raise of
Program_Error afterward if Buffer_Overflowed (Buf) is True after the
call on Put_Image (Buf, Arg)}.
!discussion
See !problem and !proposal.
We have added a reminder that private types have the same implementation
of Put_Image as their full type. Another choice would be to say that
Put_Image is not defined on a private type unless explicitly provided,
though that would clearly make composability more of a pain, analogous
to what happens with 'Write on limited types. Clearly any implementor of
a private type should specify Put_Image if they are concerned about the
loss of information hiding. For compatibility reasons we can't really
change what Put_Image does on the full type, and making the run-time
semantics of Put_Image depend on where you invoke it seems like a
generally bad idea (as well as requiring defining a new kind of aspect
with its own set of rules).
One could imagine other rules, such as hiding 'Image, but not hide
'Put_Image, on a private type. But there is nothing in the default
'Image that can't be learned from reading the specification of the
private type, so the benefit would come from having results of 'Image
not be affected by changes to the implementation of the private type.
Users with a need for stable image results can define their own
Put_Image to avoid such output instability.
We decided to have operations on the Bounded Buffer_Type
set the Buffer_Overflowed flag rather than raise Constraint_Error,
but the user of the buffer can just as easily use a pragma Assert (not
Buffer_Overflowed (Buf)) afterward. To exactly specify the conditions
when Constraint_Error would be raised would significantly complicate the
interface, and not really provide any more safety. It would also
interfere with a common situation where if an Image exceeds a maximum
desired length, you are willing to use ellipses ("...") to indicate
overflow.
As mentioned in the !proposal, although the original goals might
have included supporting 'Value, we are no longer trying to support the
implementation of user-defined 'Value operations with the current
Text_Buffers. We believe a number of other operations would be needed
to support the kind of parsing that would be needed for complex
'Value operations. We leave that for a future standard.
We considered providing a Character_Count function, to support this sort
of use case:
X : constant Text_Buffer :=
(Length => Character_Count (Buf), Data => Get (Buf));
but alas each of the various *_Get functions might produce a
different length array, so rather than having five separate
*_Character_Count functions, we recommend the use of a local rename of
the call:
Chars : String renames Get (Buf);
X : constant Text_Buffer :=
(Length => Chars'Length, Data => Chars);
One interesting side issue with the example above is that as written the
first aggregate could violate RM 6.4.1(6.18/3-6.19/3), because Get takes
Buf as an in in-out parameter, and the result would depend on whether
Character_Count were called before after the call on Get. But it is
actually OK because presumably Length is a discriminant and Data has a
per-object constraint, meaning that the expression defining Data isn't
evaluated until after Character_Count returns (see RM 4.3.1(19/5)).
!ASIS
No ASIS effect.
!ACATS test
An ACATS C-Test is needed to check the various subprograms and the image
attributes are implemented as expected.
!appendix
From: Tucker Taft
Sent: Friday, October 9, 2020 3:18 PM
Here is a new version of AI12-0384 (-2 version), which is a fixup relative
to draft 26 of the Ada 202X RM
(http://www.ada-auth.org/standards/2xaarm/html/AA-A-4-12.html).
[This is draft /01 of the AI - Editor.]
This version received a tentative "thumbs up" from Randy and Raphaël. We will
be considering this AI for approval at the upcoming ARG meeting October 21st.
Main simplifications relative to prior version of AI12-0384 (-1 version) include:
* eliminating all *Get procedures in favor of functions (which were all moved
into the Unbounded/Bounded children as they were not needed by Put_Image
itself);
* eliminating user control over character substitutions, with Get for String
and Wide_Get for Wide_String incorporating whatever substitutions
implementations do now for 'Image and 'Wide_Image;
* eliminating Position_In_Line as that was felt too "pretty print-ish"
* eliminating Constraint_Error on overflow in Text_Buffers.Bounded (and
associated Pre aspects) in favor of a "Buffer_Overflowed" flag which can be
tested after Put_Image;
* eliminating Character_Count function.
Additions relative to the draft 26 of the Ada 202x RM (most were already in
AI12-0384-1) include:
* Indentation control
* Put/Get_UTF_8 and Wide_Put/Get_UTF_16
* Buffer_Overflowed flag (mentioned above) in Bounded child
Rather than showing piecemeal changes to the package specs, I included a
complete new set of specs in this AI, to ease readability.
****************************************************************
From: Arnaud Charlet
Sent: Saturday, October 10, 2020 2:57 PM
> Here is a new version of AI12-0384 (-2 version), which is a fixup relative
> to draft 26 of the Ada 202X RM
> (http://www.ada-auth.org/standards/2xaarm/html/AA-A-4-12.html).
>
> This version received a tentative "thumbs up" from Randy and Raphaël. We
> will be considering this AI for approval at the upcoming ARG meeting
> October 21st.
Thanks for moving things forward.
> Main simplifications relative to prior version of AI12-0384 (-1 version)
> include:
>
> * eliminating all *Get procedures in favor of functions (which were
> all moved into the Unbounded/Bounded children as they were not needed
> by Put_Image itself);
I don't see a realistic use case for the bounded version, so am in favor of
removing the bounded part. The rest looks OK at first sight, I need to look
deeper into it.
Note that if a real use case is found by actual users for a bounded version,
we can always add it later, while removing something once it's standardized
is much more difficult.
We can discuss it live but my understanding is that the bounded version is
meant for constrained embedded targets where memory allocation is difficult.
But these same constrained environments already do not support
wide_wide_characters and do not support all the runtime support needed by
Put_Image. In other words, these constrained environment will not benefit from
the Put_Image feature in the first place which is very heavy in terms of
runtime support. The less constrained embedded environments which can afford
the whole Put_Image support can also afford the unbounded version.
Ergo, the bounded version isn't needed and is only adding complexity.
****************************************************************
From: Richard Wai
Sent: Saturday, October 10, 2020 11:57 AM
> I don't see a realistic use case for the bounded version, so am in favor
> of removing the bounded part. The rest looks OK at first sight, I need to
> look deeper into it.
I do sympathize with your argument here, but to play devil's advocate: I
think there can be real use-cases for the bounded case in the non-embedded
domain. I see three primary (and complimentary) lines of reasoning. One is
security; since we're talking about user-defined Image attributes, it is
very possible that the output of a given 'Image may be dependent on some
external input, or combination of inputs, and might be quite complex. There
are plenty of DoS attacks regimens that can take advantage of unbounded
memory allocation through these kind of avenues. Bounded types are really
good at protecting against this class of attack. The other line is
performance. Bounded types of high variability and long lifetimes, statically
or dynamically allocated, can improve cache tenancy. Thirdly, and related to
the second, for very high-volume and/or long-lived systems, bounded types can
potentially help reduce memory fragmentation, particularly when such a
program is designed to use mostly bounded types and near-program-lifetime
allocations.
Admittedly these are very specific and narrow use-cases, so I accept that it
is not the strongest argument.
However there is also the argument for consistency. Ada tends to provide
Unbounded/Bounded for these kinds of language-defined types/packages. For me
personally, that is possibly a stronger argument for the bounded case than
the above.
> Note that if a real use case is found by actual users for a bounded version,
> we can always add it later, while removing something once it's standardized
> is much more difficult.
I could make the case that we are an "actual user" (we do often use bounded
types for security and performance reasons), but this is a discussion we've
had before, and might not see eye-to-eye on. I will reiterate for the record,
that Ada users are a superset of FSF GNAT users, who are a superset of AdaCore
users. So I think there should be caution using this as an argument against
including the Bounded case.
> Ergo, the bounded version isn't needed and is only adding complexity.
I'm not convinced the bounded case would be overly complex to implement.
****************************************************************
From: Arnaud Charlet
Sent: Saturday, October 10, 2020 12:20 PM
> I do sympathize with your argument here, but to play devil's advocate:
OK, it's fun and fair game to play devil's advocate.
So now that you've done that: are you convinced by your devil's advocate
arguments? :-)
> I could make the case that we are an "actual user" (we do often use
> bounded types for security and performance reasons), but this is a
> discussion we've had before, and might not see eye-to-eye on. I will
> reiterate for the record, that Ada users are a superset of FSF GNAT
> users, who are a superset of AdaCore users.
I'm afraid that's wishful thinking when it comes to Ada 2020 where all users
will be GNAT users.
> > Ergo, the bounded version isn't needed and is only adding complexity.
>
> I'm not convinced the bounded case would be overly complex to implement.
I'm talking added complexity: anything extra we add does add complexity and
better be well justified and worth it. I don't see that as being the case here
and furthermore it's very easy to let implementation freedom here if users
indeed do actually need this feature in the future without dictating before
hand without a real(istic) use case.
Frankly in terms of library, we'd better spend our time on other things,
starting with providing real string (UTF8) manipulation routines as can be
found in all other languages and as badly missing to many users, as well as
many other generally useful libraries rather than added a bounded version of
text_buffer for Put_Image.
****************************************************************
From: Richard Wai
Sent: Saturday, October 10, 2020 12:41 PM
> So now that you've done that: are you convinced by your devil's
> advocate arguments? :-)
Yes. I think they are stronger arguments for bounded than yours against, to be
explicit.
> I'm afraid that's wishful thinking when it comes to Ada 2020 where all users
> will be GNAT users.
Enjoy that while it lasts!
In any case it is dangerous to make language design decisions based on the
(possibly naive) declaration of dominance by a single vendor.
I don't see why AdaCore can't just leave the bounded case out of their
implementation - this is a route that has already been taken with parallel.
****************************************************************
From: Arnaud Charlet
Sent: Saturday, October 10, 2020 1:01 PM
> I don't see why AdaCore can't just leave the bounded case out of their
> implementation - this is a route that has already been taken with parallel.
That's indeed what we'll likely do if we can't agree although that's certainly
not our preferred option, it's not like we enjoy leaving Ada 202x features
out, we're only doing it for good reasons.
****************************************************************
From: Jean-Pierre Rosen
Sent: Saturday, October 10, 2020 12:41 PM
> I'm afraid that's wishful thinking when it comes to Ada 2020 where all
> users will be GNAT users.
I am extremely concerned by that statement.
1) PTC has an Ada 2012 compiler, and might want to keep up
2) It would be extremely harmful to Ada to consider that anyway, it's just
AdaCore's private language.
****************************************************************
From: Arnaud Charlet
Sent: Saturday, October 10, 2020 1:03 PM
> I am extremely concerned by that statement.
> 1) PTC has an Ada 2012 compiler, and might want to keep up
Right, and perhaps the ARG should ask them on their view on this feature.
> 2) It would be extremely harmful to Ada to consider that anyway, it's
> just AdaCore's private language.
If it were AdaCore's private language we wouldn't be discussing here.
Ada is clearly an official and ISO language and that's a great property.
****************************************************************
From: Tucker Taft
Sent: Saturday, October 10, 2020 4:14 PM
The Bounded child is used when the Max_Image_Length restriction is in place.
See Ada 202X RM H.4(23.20/5) and H.4(23.e/5). If AdaCore chooses not to
support that restriction, then they could also choose not to support the
Bounded child, I suppose.
In my experience it is pretty frequent to want to bound the output,
particularly for debugging, but also for cases when producing some kind of
user-oriented message where messages beyond a certain length are not user
friendly. CodePeer, for example, does this in several places. So it isn't
just for resource-constrained environments.
****************************************************************
From: Arnaud Charlet
Sent: Sunday, October 11, 2020 12:59 PM
> The Bounded child is used when the Max_Image_Length restriction is in place.
> See Ada 202X RM H.4(23.20/5) and H.4(23.e/5). If AdaCore chooses not to
> support that restriction, then they could also choose not to support the
> Bounded child, I suppose.
OK. So what about we move the Bounded child to annex H then since the two
indeed go together?
> In my experience it is pretty frequent to want to bound the output,
> particularly for debugging, but also for cases when producing some kind of
> user-oriented message where messages beyond a certain length are not user
> friendly. CodePeer, for example, does this in several places. So it isn't
> just for resource-constrained environments.
OK but in this case, you can produce that with the unbounded version, you
don't need a bounded version for that, and you will in general want to have a
smart truncate at the proper place rather than at a random upper bound.
****************************************************************
From: Tucker Taft
Sent: Sunday, October 11, 2020 1:11 PM
>> The Bounded child is used when the Max_Image_Length restriction is in
>> place. See Ada 202X RM H.4(23.20/5) and H.4(23.e/5). If AdaCore chooses
>> not to support that restriction, then they could also choose not to support
>> the Bounded child, I suppose.
>
> OK. So what about we move the Bounded child to annex H then since the
> two indeed go together?
It seems like we are making this overly complicated. The point is to
establish a standard, and the Bounded child has value beyond
safety-critical systems. Implementing this child is pretty trivial. The
point is to have a standard rather than having everyone reinventing the
wheel in slightly different ways.
>> In my experience it is pretty frequent to want to bound the output,
>> particularly for debugging, but also for cases when producing some kind of
>> user-oriented message where messages beyond a certain length are not user
>> friendly. CodePeer, for example, does this in several places. So it isn't
>> just for resource-constrained environments.
>
> OK but in this case, you can produce that with the unbounded version,
> you don't need a bounded version for that, and you will in general
> want to have a smart truncate at the proper place rather than at a random
> upper bound.
Not sure what you mean by a random upper bound. Anyone who declares their own
Bounded buffer object can specify their own upper bound by specifying the
discriminant.
****************************************************************
From: Brad Moore
Sent: Tuesday, October 13, 2020 10:44 AM
I think what Arno is suggesting, or at least is a feature I have been thinking
about, is that, for an object like a container, one might want to limit the
output to N container elements (followed by an ellipsis), rather than using
the bounded container limit, which might be difficult for the user to
anticipate how to translate a bounded buffer size into nicely aligned output.
What you would likely see is that the container output is truncated randomly
in the middle of an element output, particularly if the element is a
composite object, which would be undesirable.
I am wondering if somehow an element limit could be incorporated into the
interface, perhaps as an aspect, rather than relying on the bounded buffer
size to clip the results.
e.g. for a container of fruit, with "Element_Limit" set to 3, might produce
the string, '("Banana", "Pear", "Apple", ...)'
This would work nicely and consistently for a bounded or unbounded text
buffer. If the Fruit component is later modified to change the size of the
component, it won't affect the output, whereas when using a bounded text
buffer to limit the output, this likely would affect the output.
Alternatively, or in addition, a subprogram of the container could be provided
to set the element limit.
I also think with respect to containers, there are two "views" one might want
to present. If one cares about the inner workings of the container, and wants
to see the details of the container object itself, (e.g. for a hash table, all
the buckets and bucket contents, etc) one might want to present a "verbose"
view. However, if one isn't debugging the container, (most likely the typical
case) and is only interested in the elements that the container holds, a
verbose=false would be useful, which would produce ouytput like the fruit
container output above.
So perhaps a Verbose aspect would be useful here as well.
But Again, alternatively or in addition, a subprogram of the container could
be provided to set the verbose flag.
I am not sure if these are things that should be left for a future standard
version, or should be considered now while we are hammering out the details
of this interface.
****************************************************************
From: Tucker Taft
Sent: Tuesday, October 13, 2020 4:13 PM
I can see some value for this, but it would be adding significant complexity,
given that you could have containers within containers, or arbitrary tree
structures. At this point we are trying to provide basic functionality with
a relatively simple interface, not make the interface even more complex. In
my experience, in many applications it makes sense to just chop, or
potentially chop back to some punctuation. Anything more complex than that
is generally quite application and data-structure specific.
In an interactive graphical user interface, you can expand and contract
individual levels, but that is not what we are talking about here.
****************************************************************
From: Brad Moore
Sent: Tuesday, October 13, 2020 11:16 PM
For what it's worth, Python has this capability, which is described in their
main tutorial here, in their tutorial section on output formatting.
https://docs.python.org/3/tutorial/stdlib2.html#output-formatting
See the first two examples, which are similar to the capabilities that we are
providing in this AI.
Their approach is that there is a default element limit that gets applied to
container classes. So the setting would apply recursively to containers within
containers.
So the interface for this seems like it could potentially be very simple.
Perhaps the interface would just be a procedure, aspect, or configuration
pragma to override the default, e.g. Set_Max_Image_Element_Count ( Count
:= 0), where the default of zero is unlimited (except by other limits such
as bounded buffer size). Otherwise, the exposed interface to support this
would be exactly as you have already described in the AI. Implementations
could perhaps choose not to support the Set_Max_Image_Element_Count
procedure, if the implementation burden for that is too significant, I
suppose.
****************************************************************
From: Randy Brukardt
Sent: Tuesday, October 13, 2020 11:52 PM
Well, you'd certainly have to have a way to *read* the value (else how could
a Put_Image routine use it?), and moreover, to make it useful you'd have to
have a use of it for the default image for all (or at least nearly all)
composite types (it makes no sense to treat containers and arrays differently,
unclear what it should mean for records). Not that many people are going to be
writing their own Put_Image implementations.
If it only applied to user-defined Put_Image routines, then there isn't any
reason to include it in the Standard, since the user can just as well define
their own extension of the Text_Buffer to include whatever extra features they
need. (Some people seem to expect that to be common; I don't, but either way
it would seem to be premature standardization to define such features ONLY for
user-defined routines.)
****************************************************************
Questions? Ask the ACAA Technical Agent