Version 1.1 of ai12s/ai12-0340-1.txt
!standard A.4.12(0) 19-09-03 AI12-0340-1/01
!standard 4.10(3.1/5)
!standard A.10(3.2/5)
!standard A.10(6/5)
!standard A.10(25.2/5)
!standard A.10(28/5)
!standard A.10(31/5)
!class Amendment 19-09-03
!status work item 19-03-03
!status received 19-06-20
!priority Medium
!difficulty Easy
!subject Put_Image should use a Text_Buffer
!summary
A set of universal text buffer packages is introduced, and Put_Image is
changed to use it rather than streaming.
!problem
The definition of Put_Image to use a stream parameter is error-prone, since
it only works when a Wide_Wide_String is streamed to the passed in storage
stream. If a String (rather than a Wide_Wide_String) is passed by mistake,
garbage results will occur.
!proposal
(See Summary.)
!wording
Add a new subclause:
A.4.12 Universal text buffers
A universal text buffer can be used to save and retrieve text of any
language-defined string type. The types used to save and retrieve the text
need not be the same.
The text buffer packages have the following declarations:
[Author's note: See the !discussion for discussion of design issues.]
package Ada.Strings.Text_Buffers 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 private;
function Character_Count (Buffer : Root_Buffer_Type)
return Text_Buffer_Count is abstract;
procedure Clear (Buffer : Root_Buffer_Type) is abstract
with Post'Class => Character_Count (Buffer) = 0;
procedure Get (
Buffer : in out Root_Buffer_Type;
Item : out String;
Last : out Natural) is abstract
with Post'Class =>
(declare
Num_Read : constant Text_Buffer_Count :=
Text_Buffer_Count'Min
(Character_Count(Buffer)'Old, Item'Length);
begin
Last = Num_Read + Item'First - 1 and then
Character_Count (Buffer) =
Character_Count (Buffer)'Old - Num_Read);
procedure Wide_Get (
Buffer : in out Root_Buffer_Type;
Item : out Wide_String;
Last : out Natural) is abstract
with Post'Class =>
(declare
Num_Read : constant Text_Buffer_Count :=
Text_Buffer_Count'Min
(Character_Count(Buffer)'Old, Item'Length);
begin
Last = Num_Read + Item'First - 1 and then
Character_Count (Buffer) =
Character_Count (Buffer)'Old - Num_Read);
procedure Wide_Wide_Get (
Buffer : in out Root_Buffer_Type;
Item : out Wide_Wide_String;
Last : out Natural) is abstract
with Post'Class =>
(declare
Num_Read : constant Text_Buffer_Count :=
Text_Buffer_Count'Min
(Character_Count(Buffer)'Old, Item'Length);
begin
Last = Num_Read + Item'First - 1 and then
Character_Count (Buffer) =
Character_Count (Buffer)'Old - Num_Read);
function End_of_Line (Buffer : in Root_Buffer_Type)
return Boolean is abstract;
procedure Put (
Buffer : in out Root_Buffer_Type;
Item : in String) is abstract
with Post'Class =>
Character_Count (Buffer) =
Character_Count (Buffer)'Old + Item'Length;
procedure Wide_Put (
Buffer : in out Root_Buffer_Type;
Item : in Wide_String) is abstract
with Post'Class =>
Character_Count (Buffer) =
Character_Count (Buffer)'Old + Item'Length;
procedure Wide_Wide_Put (
Buffer : in out Root_Buffer_Type;
Item : in Wide_Wide_String) is abstract
with Post'Class =>
Character_Count (Buffer) =
Character_Count (Buffer)'Old + Item'Length;
procedure New_Line (Buffer : in out Root_Buffer_Type)
is abstract
with Post'Class =>
Character_Count (Buffer) =
Character_Count (Buffer)'Old + New_Line_Count;
end Ada.Strings.Text_Buffers;
package Ada.Strings.Text_Buffers.Unbounded
with Preelaborated, Nonblocking, Global => null is
[Author's note: The Global => null means that Buffer_Type will typically
be compound, and any access types used will be internal.]
type Buffer_Type is new Root_Buffer_Type with private
with Default_Initial_Condition =>
Character_Count (Buffer_Type) = 0;
--
--
end Ada.Strings.Text_Buffers.Unbounded;
package Ada.Strings.Text_Buffers.Bounded
with Pure, Nonblocking, Global => null is
type Buffer_Type (Max_Characters : Text_Buffer_Count)
is new Root_Buffer_Type with private
with Default_Initial_Condition =>
Character_Count (Buffer_Type) = 0;
--
--
--
--
--
--
--
--
--
end Ada.Strings.Text_Buffers.Bounded;
Character_Count returns the number of characters currently stored in a text
buffer.
AARM Ramification: This is lower-case "characters". The representation isn't
considered, so it is irrelevant what type of character (Character,
Wide_Character, or Wide_Wide_Character) was stored.
New_Line stores New_Line_Count characters that represents 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.
A call to Put, Wide_Put, or Wide_Wide_Put stores a sequence of characters into the
text buffer.
A call to Get, Wide_Get, or Wide_Wide_Get returns the same sequence of
characters as was present in the calls that stored the characters into the buffer.
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 the
any character in the sequence is not defined in Wide_Character, the result is
implementation-defined.
[Author's note: The postcondition on these routines does not allow the length to
be different from that of the sequence of characters. That's different than the
current definition of Image and Wide_Image. Is that a problem? If so, we'll have
to weaken the postcondition (to allow longer results than the number of characters
returned) and add some more English to require the length of result to equal the
characters read if all characters in the sequence are in the appropriate type. Or
I suppose we could add a pair of queries to that effect.]
=========================
Replace 4.10(3-3.2/5) with:
S'Put_Image S'Put_Image denotes a procedure with the following specification:
procedure S'Put_Image
(Buffer : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
Arg : in T);
The default implementation of S'Put_Image writes (using Wide_Wide_Put) an image
of the value of Arg.
Replace 4.10(6/5) with:
procedure Scalar_Type'Put_Image
(Buffer : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class;
Arg : in Scalar_Type) is
begin
Buffer.Wide_Wide_Put (<described below>);
end Scalar_Type'Put_Image;
[Author's note: I used prefix notation here so I don't have to fully
qualify the name of the call, which would be a LOT longer:
Ada.Strings.Text_Buffers.Wide_Wide_Put (Buffer, <described below>);]
Replace 4.10(25.2/5) with:
Wide_Wide_Image calls S'Put_Image passing Arg (which will typically store
a sequence of character values out in a text buffer) and then returns the
result of retrieving the contents of that buffer with Wide_Wide_Get. The
lower bound of the result is 1.
Replace 4.10(28/5) with:
Wide_Image calls S'Put_Image passing Arg (which will typically store
a sequence of character values out in a text buffer) and then returns the
result of retrieving the contents of that buffer with Wide_Get. The
lower bound of the result is 1.
Replace 4.10(31/5) with:
Wide_Image calls S'Put_Image passing Arg (which will typically store
a sequence of character values out in a text buffer) and then returns the
result of retrieving the contents of that buffer with Get. The
lower bound of the result is 1.
[Author's note: The correspondence between Wide_Wide_Image, Wide_Image, and
Image is now implicit in the definitions of Put_Image and the text buffer
retrieval routines. That should make it easier to use similar definitions
elsewhere in the future.]
Replace H.4(23.2/5) with:
Max_Image_Length
Specifies the maximum length for the result of an Image, Wide_Image, or
Wide_Wide_Image attribute. Violation of this restriction results in the
raising of Program_Error at the point of the invocation of an image attribute.
[Bairdian Author's note: This definition will not be quite right if we allow
longer results from Image than from Wide_Wide_Image (Ada 95-2012 does, the
definition given here does not). We would have to talk about the number of
characters (lower case) in the result of Wide_Wide_Image and then state that
a similar restriction applies to Image and Wide_Image. (Or something like
that.) Note that the AARM note deleted below is a lie in the case when the
Image result is longer than Wide_Wide_Image for the existing definition -
the goal was to bound the buffer, not the result of Image (which can be longer).]
Delete AARM H.4(23.a.1/5).
Modify H.4(23.11/5):
If a Max_Image_Length restriction applies to any compilation unit in the
partition, then for any subtype S, {S'Image, S'Wide_Image, and}
S'Wide_Wide_Image shall be implemented within that partition without any
dynamic allocation.
!discussion
The basic outline of the text buffer packages is copied from the Storage
streams packages. These are "Buffers" rather than "Streams", and the counts
are in terms of "characters" rather than "elements". We don't call these a
stream since these are type-safe and will do appropriate conversions as
necessary.
We considered having some of the operations placed into subpackages. However,
since these operations need to be usable from dispatching calls (so that both
the Unbounded and Bounded versions can be used from the implementation of
Put_Image), they all have to be primitive for the abstract type.
We could have used a family of interfaces rather than the single root type
proposed here. However, the implementations of Unbounded and Bounded
Text_Buffers have to provide all of the operations, so doing that doesn't seem
to save any effort. But using multiple interfaces could be more expensive in
time and space than a single interface (because of the more complex dispatching
table).
We could have used a single interface rather than the single root type, but
that mainly would prevent any implementation sharing between the Bounded and
Unbounded implementations (as an interface can't have concrete routines, while
an abstract type can).
We provided a set of Text_IO-like operations on strings. We provide New_Line
so that line breaks can be conveniently added to large Images. Completeness
requires that End_of_Line be provided for retrieval. We left out Put_Line and
Get_Line as those seemed like overkill and would make a simple interface
rather large. We also omitted Skip_Line as the Postcondition is rather
complex.
We can't use overloading for Put, as if we did, all string
literals would be ambiguous in calls. These are likely to be common in
Put_Image implementations and it doesn't make sense for those to be hard.
We named Get similarly, even though overloading would not be
a problem for them. Having different naming styles for reading and writing
would be too confusing.
For Put_Image and the use in the Image attribute family, we only need the
various Put routines, Count, New_Line, and the various Get routines. (Note
that Get is defined to do the same conversion as Image is defined to do to
Wide_Wide_Image, so no separate conversion code is needed - the attribute can
simply call the appropriate Get to retrieve the results.) The other routines
are provided for completeness and consistency with Text_IO.
Note that we don't define the internal storage format, rather just talking
about storage of characters. The Get routines are responsible for converting
the internal format to the appropriate output format. In particular, an
implementation could use UTF8 storage to save space, or Wide_Wide_String
storage to be simple.
----
We could imagine defining UTF8_Get, UTF8_Put, UTF16_Get, and UTF16_Put. We
didn't do this to keep the size of these packages down. In addition, the
definition of Last for UTF8_Get and UTF16_Get seems to be impossible with the
operations available in UTF_Encoding. That makes it impossible to describe
the operations with Post'Class definitions.
[Author's note: We could of course use English for that, but we're trying to
keep that to a minimum here. If there is enough interest in these operations,
we certainly can do that.]
!ASIS
No ASIS changes needed.
!ACATS test
ACATS B- and C-Tests are needed to check that the new library exists, works
as defined, and is used by the default implementation of Image.
!appendix
From: Tucker Taft
Sent: Thursday, June 20, 2019 12:22 PM
>> ...
>> Might be clearer
>> if you called the routine "Put_Image" rather than "Image".
>
> Agreed. Why confuse the usage (usually a routine "Image" returns a String).
[Editor's note: The quoted text comes from a thread associated with AI12-0312-1.]
By the way, we have had an internal comment that Put_Image as specified takes
a stream, but in fact it probably fails miserably if you write anything but
Wide_Wide_Character and Wide_Wide_String on the stream. That admittedly seems
pretty error-prone.
The suggestion is that we define a new kind of "Text_Stream," probably in
Text_IO.Text_Streams, which allows you to write/read any size of character, or
any string thereof, and it does the "right" thing, as far as storing the
characters as Wide_Wide_Characters, or more efficiently, using UTF8. 'Image
would be defined using a Storage_Text_Stream presumably. There would probably
also be a New_Line procedure and an End_Of_Line function so that you don't
have to write Wide_Wide_Character'(ASCII.LF) or equivalent to create a
multi-line image.
A Text_Stream would presumably be an extension of Root_Stream_Type, and
function stream of *Text_IO.Text_Streams might actually return an access
Text_Stream'Class instead of what it is returning now.
Clearly needs some work, but something like this could reduce the errors
inherent in the current model, where only Wide_Wide_Character and
Wide_Wide_String actually work correctly with a Put_Image stream. Brad, for
example, seems to tripped over this in his example.
****************************************************************
From: Randy Brukardt
Sent: Thursday, June 20, 2019 11:26 PM
Two thoughts on this one (not well thought out yet).
(1) The reason that we switched to the streaming model was to avoid overhead.
Adding an additional level of dispatching calls clearly adds overhead (and a
lot of it if UTF-8 is used internally).
(2) This description seems to be confusing a stream (which is an I/O or
buffering concept on raw bytes with a very simple interface), and the
somewhat higher level features that read/write a stream or buffer (the stream
attributes).
If this is a serious concern (which is not obvious to me, few people ought to
need to write Put_Images), then clearly streaming is the wrong model for
Image. With this concept, there can't be any streaming involved in order that
the overhead remain manageable (a multi-level way would be crazy). That makes
this suggestion something different (essentially, a fancy text buffer), and if
it goes forward it should be named and described differently (calling it a
stream would just be confusing). It might make sense to allow this buffer to
(optionally) be read/written to a stream, but that wouldn't be the primary
purpose or use. (That also would solve the Global problem for it and 'Image,
as a text buffer does not need to read/write any globals.)
Sounds like a substantial job, though, and a complete redo of the Image stuff
(which was very painful the first time). And we'd lose the main justification
for the Fifo_Streams -- should they even be kept around??
****************************************************************
From: Randy Brukardt
Sent: Friday, June 21, 2019 1:32 AM
BTW, Ada already has something called Text_Streams - see A.10.1 . And that's
more in line with streaming than this proposal.
****************************************************************
From: Brad Moore
Sent: Saturday, June 22, 2019 1:45 AM
I have some thoughts on this issue as well. I like Tucker's suggestion of a
special kind of queue
First some observations. The Streams mechanism appears to be much broader
machinery than what is needed. We really need only a small subset of
Ada.Streams capabilities.
1) The programmer only need a stream that supports writes. The implementation
hides the reading of the stream, and provides the actual stream object and
class. The implementation doesn't even need to be use the streaming mechanism
to pull the data out of the stream. At that point it is effectively just an
array of wide-wide characters, which could be extracted directly.
2) The programmer only needs a stream that supports writes of one type,
wide-wide-characters. The streaming mechanism allows writes of most any type.
It seems what we really want is a queue that mostly acts like a stream, but
where all objects in the queue are of the same type (homogeneous).
I have written a composable set of queues/deques generics called dequesterity
which can be found on SourceForge, that support many different types of queues
including streaming queues, and just regular queues. They all have mostly a
common interface, but only the streamable queues allow me to use the 'Read and
'Write attributes, which seems like an unfortunate limitation.
It would be nice if 'Read and 'Write attributes could be generalized to support
other types of queues (homogeneous ones).
Here are some ideas floating around in my head.....
Ada.Containers.Synchronized_Queue_Interfaces seems much closer to what we
need, than Ada.Streams.
Perhaps we could use that, or if we don't want synchronization, define a
similar Ada.Containers.Unsynchronized_Queue_Interfaces package?
What if we could apply 'Read and 'Write to such containers based on such
interfaces to read and write objects of Element_Type?
We could create a standard instance using Wide_Wide_Character, e.g.
package Ada.Containers.Wide_Wide_Character_Queues is
new Ada.Containers.Synchronized_Queue_Interfaces
(Element_Type => Wide_Wide_Character);
And then use an object of;
not null access Ada.Containers.Wide_Wide_Character_Queue.Queue'Class
as the parameter of the call associated with 'Put_Image.
'Read would map to a call to Dequeue, and 'Write would map to a call to
Enqueue.
This way, the compiler could ensure statically that the only
Wide_Wide_Characters are being written for Put_Image.
We'd get to use 'Read and 'Write more generally, and could potentially extend
to be applied to user-defined homogeneous queues.
Its also nice that a programmer could use the same subprograms they'd write
for this specialized streaming function to also work with regular Ada.Streams
objects.
Anyway, there may be giant holes in this idea, and obviously needs to be
fleshed out further but I think it could fit in with Tucker and Randy's
thoughts, and it at least seems workable to me....
****************************************************************
From: Tucker Taft
Sent: Saturday, June 22, 2019 1:28 PM
>BTW, Ada already has something called Text_Streams - see A.10.1.
Yes, I realized that, and was suggesting this new kind of stream might be
added to that existing package.
>And that's more in line with streaming than this proposal.
It is solving a different problem, namely creating a stream from an existing
open Text_IO.File. This new "text" stream would have an interface that had
some additional operations, some of which might be class-wide rather than
dispatching, conceivably, to unify normal, wide, and wide-wide character
streaming into something like UTF8, or some other universal format (UTF16,
UCS32). In other languages, there are various "wrappers" you can apply to
a stream to create a buffered stream, a formatted stream, etc. Clearly this
is a bigger topic than just Put_Image, but Put_Image seems to have brought it
to the fore.
****************************************************************
From: Randy Brukardt
Sent: Monday, June 24, 2019 5:45 PM
...
> It would be nice if 'Read and 'Write attributes could be generalized
> to support other types of queues (homogeneous ones).
I don't see how that could work. These attributes take a stream parameter and
use dispatching operations of a stream to do their work. You seem to be
suggesting that they also somehow take a queue parameter instead and
presumably use dispatching operations of a queue to do their work.
That could only happen if the entire thing was duplicated (overloaded). I
suppose a compiler could do that, but it seems like a lot of overhead. And I
have no idea how a user-defined 'Read or 'Write would work in such a case - it
would seem that two routines would be needed.
If you somehow tried to use the stream interface to read/write to the queues,
you'd lose any type checking, which would seem to defeat the purpose.
****************************************************************
From: Randy Brukardt
Sent: Monday, June 24, 2019 6:47 PM
...
>>. And that's more in line with streaming than this proposal.
>
>It is solving a different problem, namely creating a stream from an
>existing open Text_IO.File. This new "text" stream would have an
>interface that had some additional operations, some of which might be
>class-wide rather than dispatching, conceivably, to unify normal, wide,
>and wide-wide character streaming into something like UTF8, or some
>other universal format (UTF16, UCS32).
This is indeed what I thought you were talking about, and while I have little
objection with the concept, it seems wrong to call it a "stream" (or to use
Ada streams) with such a construct. This objection stems from the way Ada
currently uses these terms, but also in that you are making something that
can and is safe into something unsafe and much more error-prone.
>In other languages, there are various "wrappers" you can apply to a
>stream to create a buffered stream, a formatted stream, etc. Clearly
>this is a bigger topic than just Put_Image, but Put_Image seems to have
>brought it to the fore.
Given the design of Ada Streams, this is bass-ackwards. Ada Streams is a
completely untyped, you're on your own mechanism. It is hard to avoid causing
erroneous execution using Ada Streams. One cannot build safe abstractions on
top of such a basis unless you can totally hide the underlying mechanism. But
Ada doesn't let one hide underlying abstractions; they're pretty much only
additive. (Raising an exception in this case wouldn't work, either, as one
would need the underlying abstraction to implement the guy.)
And there is absolutely no reason for a text buffer to have the possibility of
erroneous execution. If one sticks a bunch of strings into a buffer, the result
can be read as one or more strings. (I'm ignoring any meaning, of course - that
an't make a program become erroneous.)
There's also the obvious possibility of confusion. If you have the Write
attribute and a Write subprogram, it's easy to imagine getting them mixed up.
(Choosing the names better could help a bit, but not a lot -- people are used
to using 'Write to write a stream, but here that would be wrong.) And if the
buffer is converting to UTF-8 internally, they are going to do something
pretty different for every built-in Ada type.
I would much prefer to see a strongly typed interface that doesn't allow
anything other that strings into and out of the buffer, especially if you are
doing conversions. Why encourage misuse?? I could see allowing the buffer to
be streamed into or out of (which would be untyped, of course), but not
treating it as an Ada Stream.
...
>>then clearly streaming is the wrong model for Image.
>I think a stream is OK, but the question is how is the
>stream interpreted.
If you are interpreting a stream as anything other than a raw stream of bytes,
you are making matters worse, not better.
>I would have presumed that a typical use of Put_Image would be to give
>it a stream from, say, Text_IO.Text_Streams.Stream, and then directly
>put the image onto the stream rather than doing a Put(File,
>Blah'Image). But that probably won't work.
>In fact, I think as defined you would have to use
>Wide_Wide_Text_IO.Text_Streams.Stream to produce a stream that would
>work with Put_Image.
I can't imagine why you would mess with streams for this, even if you had to
use Text_IO (and that's not that likely). Dragging in Text_IO is a massive
pile-o-stuff that one doesn't want to require in Ada programs.
>I am thinking that perhaps Wide_Wide_Text_IO.Text_Streams,
>Wide_Text_IO.Text_Streams, and Text_IO.Text_Streams should all produce
>a stream that is "universal" in some sense, and can accept Characters,
>Wide_Characters, and Wide_Wide_Characters
>and do the "right" thing.
That would be wildly incompatible, given the existing byte stream
intepretation of these things.
Yes, there's an argument that you should have done all of this in Ada 95, but
you would have had to have known about UTF-8 and how it would end up
dominating back then to understand what to do. And had a lot more will about
abandoning type String back then, too. It's too late now, unless you want to
make a parallel runtime.
...
>>Sounds like a substantial job, though, and a complete redo of the
>>Image stuff (which was very painful the first time). And we'd lose the
>>main justification for the Fifo_Streams -- should they even be kept around??
>I think Fifo_Streams are part of the solution...
No, because they're untyped. Something very much like them could be the
solution, but it would have to be typed (text-only).
>... , but we agree that this will need more thought. I don't see this
>as a complete "redo" of the image stuff. All I am hoping is that we
>have the ability to use normal Strings/Characters rather than only
>Wide_Wide_Strings/Characters to produce an image.
"Complete" was probably too strong. It just means completely removing all of
the stream stuff from Image and using a typed text buffer instead. I have
come to think this would be a good thing, as it would solve the
Nonblocking/Global issues as well (no streams, no dispatching nonsense).
****************************************************************
From: Randy Brukardt
Sent: Monday, June 24, 2019 6:56 PM
> This is indeed what I thought you were talking about, and while I have
> little objection with the concept, it seems wrong to call it a
> "stream" (or to use Ada streams) with such a construct. This objection
> stems from the way Ada currently uses these terms, but also in that
> you are making something that can and is safe into something unsafe
> and much more error-prone.
In the interests of something positive, let me propose a specification for
a "Text_Buffer" that does something like Tucker describes. It would allow
concatenating chunks of text in whatever format as well as extracting them
in similar arbitray formats. It could have an optional streaming interface,
but it could be used without referring to any streams (and usually would be
used that way).
Presumably, there would also be a bounded version (not shown).
Something like the following would be ideal:
package Text_Buffers
with Preelaborated, Nonblocking, Global => null is
-- This Global => null requires that a Text_Buffer is a compound object,
-- and any access types be internal.
type Text_Buffer is private;
function Character_Count (Buffer : in Text_Buffer) return Natural;
procedure Clear (Buffer : in out Text_Buffer);
procedure Write (Buffer : in out Text_Buffer;
Item : in String)
with Post => Character_Count (Buffer)'Old + Item'Length =
Character_Count (Buffer);
procedure Wide_Write (Buffer : in out Text_Buffer;
Item : in Wide_String)
with Post => Character_Count (Buffer)'Old + Item'Length =
Character_Count (Buffer);
-- I hate having to use "Wide_xxx" here, but it's necessary if we're
-- going to allow using literals conveniently with this package.
procedure Wide_Wide_Write (Buffer : in out Text_Buffer;
Item : in Wide_Wide_String)
with Post => Character_Count (Buffer)'Old + Item'Length =
Character_Count (Buffer);
procedure Read (Buffer : in out Text_Buffer;
Item : in out String;
Length : in out Natural)
with Post => Character_Count (Buffer)'Old - Length =
Character_Count (Buffer);
function Read (Buffer : in out Text_Buffer) return String
with Post => Character_Count (Buffer)'Old = Read'Length and then
Character_Count (Buffer) = 0;
-- Same for Wide_String and Wide_Wide_String.
-- We also could have UTF8 and UTF16 routines (possibly in a child,
-- to avoid dragging in Ada.Strings.Encoding unless needed).
-- Similarly, we could support a streaming interface:
procedure Read_into_Buffer(
Stream : not null access Ada.Streams.Root_Stream_Type'Class;
Buffer : in out Text_Buffer;
Length : in Natural)
with Post => Character_Count (Buffer)'Old + Length =
Character_Count (Buffer)
Nonblocking => False, Global => in out all;
-- Read length Wide_Wide_Characters from Stream.
procedure Write_from_Buffer(
Stream : not null access Ada.Streams.Root_Stream_Type'Class;
Buffer : in out Text_Buffer;
Length : in Natural := 0)
with Post => (if Length /= 0 then
Character_Count (Buffer)'Old - Length =
Character_Count (Buffer)
else
Character_Count (Buffer) = 0)
Nonblocking => False, Global => in out all;
-- Write length Wide_Wide_Characters to Stream. If Length is 0,
-- write all of the characters in the buffer to the stream.
================
Put_Image would then be modified to take a Text_Buffer rather than a Stream:
procedure S'Put_Image
(Buffer : in out Text_Buffer'Class;
Arg : in T);
Thoughts and brickbats welcome.
****************************************************************
From: Tucker Taft
Sent: Monday, June 24, 2019 8:46 PM
Interesting. It looks like you are assuming that Text_Buffer is tagged. You
could imagine an implementation of Text_Buffer on top of a "standard" stream,
but you make the good point that you don't want both a "standard" stream and
a "formatted" stream interface available on the same object. The original
comment recommended providing a "New_Line" operation to allow the 'Image to
have line breaks in it. Implies you might want an End_Of_Line query as well.
So do folks feel we should move in this direction for Put_Image?
****************************************************************
From: Randy Brukardt
Sent: Monday, June 24, 2019 9:37 PM
> Interesting. It looks like you are assuming that Text_Buffer is
> tagged.
I thought I had written "tagged private", but I must have taken that out at
some point. And then depended upon it. :-(
I was presuming that we still would want a bounded version, and clearly
Put_Image has to work with both. So that implies dispatching calls, and thus
tags.
> You could imagine an implementation of Text_Buffer on top of a
> "standard" stream, but you make the good point that you don't want
> both a "standard" stream and a "formatted" stream interface available
> on the same object.
> The original comment recommended providing a "New_Line"
> operation to allow the 'Image to have line breaks in it.
> Implies you might want an End_Of_Line query as well.
Suppose that makes sense.
> So do folks feel we should move in this direction for Put_Image?
Seems to help in usage (Brad demonstrated this is easy to get wrong) and as
a side-effect reduces the issues of blocking, side-effect-full 'Image.
****************************************************************
From: Brad Moore
Sent: Tuesday, June 25, 2019 8:35 PM
> ...
>> It would be nice if 'Read and 'Write attributes could be generalized
>> to support other types of queues (homogeneous ones).
>
> I don't see how that could work. These attributes take a stream
> parameter and use dispatching operations of a stream to do their work.
> You seem to be suggesting that they also somehow take a queue
> parameter instead and presumably use dispatching operations of a queue to do
> their work.
>
> That could only happen if the entire thing was duplicated
> (overloaded). I suppose a compiler could do that, but it seems like a
> lot of overhead. And I have no idea how a user-defined 'Read or 'Write
> would work in such a case - it would seem that two routines would be needed.
Yes, I was thinking of overloading the attributes such that if the argument is
a homogeneous queue type, it would get mapped to the Enqueue/Dequeue calls,
without invoking any of the streaming machinery.
Streams are effectively heterogenous queue abstractions (they can put objects
of different types in the queue). I was suggesting using some syntactic sugar
to extend 'Read and 'Write to also work with homogeneous queue abstractions.
To the user, the semantics of a queue would be similar even though the
underlying implementation would be very different.
In any case, the syntactic sugar of using 'Read and 'Write isn't critical. If
we didn't have that, one could just call the Enqueue/Dequeue primitives
directly, which I think falls in line more or less with what you are
proposing, which is to have a special interface abstraction for this, rather
than use the streaming mechanism.
> If you somehow tried to use the stream interface to read/write to the
> queues, you'd lose any type checking, which would seem to defeat the
> purpose.
I was thinking that the compiler would enforce that only one type could be
streamed into such a queue.
The overloading of 'Read and 'Write was just a side thought, to see if it
might generate any interest.
So far it seems like it does not hold any appeal, which is Ok. What's
important is to have a mechanism that isn't error prone that doesn't feels
like sticking a round peg into a square hole.
****************************************************************
From: Brad Moore
Sent: Monday, June 24, 2019 8:37 PM
> So do folks feel we should move in this direction for Put_Image?
I also agree that this seems like a step in the right direction.
****************************************************************
Questions? Ask the ACAA Technical Agent