!standard 13.13.2 (31) 02-01-04 AI95-00260/02
!standard 13.13.2 (34)
!class amendment 01-02-13
!status work item 01-06-02
!status received 01-02-13
!priority Low
!difficulty Medium
!subject How to control the tag representation in a stream
!summary
Two new stream attributes are proposed for tagged types. These attributes
can be used to redefine the stream format of tags.
!problem
S'Class'Output writes the tag of an item with String'Output. This routine
cannot be replaced, so it is not possible to write the tag in an arbitrary
user-defined format (to match an external standard, for example).
Consider, for example, XML objects, which are a text format. With the current
language definition, it is not possible to redefine the 'Read and 'Write
attribute to have the object streamed as an XML object. This is surprising,
as external tags are defined as strings. It is possible to change the external
tag string value by using Ada.Tags facilities. But the tag will always
be streamed as an unbounded string object. This means that the tag string value
will be written preceded by the string bounds -- which are binary data. There
is no way to prevent this binary data from being be sent. In other words, there
is no way to stream an object with a plain text representation.
!proposal
Two new operational attributes are defined for reading and writing tags.
These attributes are used in S'Class'Input and S'Class'Output.
For every tagged subtype S'Class of a class-wide type T'Class, the following
attributes are defined:
S'Class'Tag_Write
procedure S'Class'Tag_Write (
Stream : access Streams.Root_Stream_Type'Class;
Tag : Ada.Tags.Tag);
S'Class'Tag_Write writes the value of Tag to Stream.
S'Class'Tag_Read
function S'Class'Tag_Read (
Stream : access Streams.Root_Stream_Type'Class)
return Ada.Tags.Tag;
S'Tag_Read reads a tag from Stream, and returns its value.
If T is a derived type, then the parent class-wide type's attributes are
called. Otherwise, the default implementation of Tag_Write is to call
String'Output(Stream, Tags.External_Tag(Item'Tag)), which is what Ada 95
defines as the way of writing tags to a stream. Similarly, the default
implementation of Tag_Read is to return the value of
Tags.Internal_Tag(String'Input(Stream)).
User-specifed Tag_Read and Tag_Write attributes may raise an exception if
presented with a tag value not in S'Class.
S'Class'Input and S'Class'Output use S'Tag_Read and S'Tag_Write, respectively,
to read and write tag values. The default implementations of S'Tag_Read and
S'Tag_Write insure that programs not redefining these attributes get unchanged
behavior.
!discussion
These attributes can be set on any class-wide tagged type, not just the root
of a class. This is important so that stream representation can be controlled
for types whose root is not in the user's control: controlled types, for
example.
We have to limit the tag values that can be presented to user-specified
Tag_Read and Tag_Write routines, as otherwise they would have to be able to
handle any tag value which might exist in the program. Similarly, we also
have to mention that the result of S'Class'Input is converted to S'Class, to
handle the case where some tag not in S'Class is presented to S'Class'Input.
The wording "returns that result" does not have this effect, as conversions
are defined as part of the execution of a return_statement. (This is a minor
hole in Ada 95, discussed in AI-279, which gets more serious with
user-defined tag reading).
Note that RM 13.13.1(31) is missing a right parenthesis in the call to
String'Output, and it is missing the Stream parameter. But this paragraph will
be replaced anyway in this proposal.
!wording:
Add after 13.13.1(28):
S'Class'Tag_Write
procedure S'Class'Tag_Write (
Stream : access Streams.Root_Stream_Type'Class;
Tag : Ada.Tags.Tag);
S'Class'Tag_Write writes the value of Tag to Stream.
S'Class'Tag_Read
function S'Class'Tag_Read (
Stream : access Streams.Root_Stream_Type'Class)
return Ada.Tags.Tag;
S'Tag_Read reads a tag from Stream, and returns its value.
The default implementations of the Tag_Write and Tag_Read operate as follows:
* If T is type derived from type P, the default implementation of Tag_Write
calls P'Class'Tag_Write, and the default implementation of Tag_Read
calls P'Class'Tag_Read;
* Otherwise, the default implementation of Tag_Write calls
String'Output(Stream, Tags.External_Tag(Tag)) -- see 3.9. The default
implementation of Tag_Read returns the value of
Tags.Internal_Tag(String'Input(Stream)).
User-specified Tag_Read and Tag_Write attributes may raise an exception if
presented with a tag value not in S'Class.
Replace 13.13.1(31) by:
First writes the external tag of Item to Stream (by calling
S'Tag_Write(Stream, Item'Tag)) and then dispatches to the subprogram denoted
by the Output attribute of the specific type identified by the tag.
Replace 13.13.1(34) by:
First reads the external tag from Stream and determines the corresponding
internal tag (by calling S'Tag_Read(Stream)) and then dispatches to the
subprogram denoted by the Input attribute of the specific type identified by
the internal tag; converts that result to S'Class and returns it.
!example
Here is an example that illustrates the problem. Let's say that we want to
stream an object with an XML encoding:
with Ada.Streams;
package Class is
type Object is tagged record
V : Integer := 2;
end record;
procedure Write (S : access Ada.Streams.Root_Stream_Type'Class;
O : Object);
for Object'Write use Write;
for Object'External_Tag use "");
end Write;
end Class;
with Ada.Text_IO.Text_Streams;
with Class;
procedure Main is
O : Class.Object;
begin
Class.Object'Class'Output
(Ada.Text_IO.Text_Streams.Stream (Ada.Text_IO.Current_Output), O);
end Main;
This program output will be something like:
^A^@^@^@^H^@^@^@
The first 8 bytes (characters) are the binary representation for the tag bound.
In Ada 95, there is no way to prevent these bytes from being written, so
there is no way to stream an object with an XML representation.
Using the new 'Tag_Read and 'Tag_Write attributes it is
possible to control the tag input/output. By changing the Class spec to read:
with Ada.Streams, Ada.Tags;
package Class is
type Object is tagged record
V : Integer := 2;
end record;
for Object'External_Tag use "object";
procedure Write (S : access Ada.Streams.Root_Stream_Type'Class;
O : Object);
for Object'Write use Write;
procedure Write (S : access Ada.Streams.Root_Stream_Type'Class;
Tag : Ada.Tags.Tag);
-- Write a tag of the form "".
for Object'Class'Tag_Write use Write;
function Read (S : access Ada.Streams.Root_Stream_Type'Class)
return Ada.Tags.Tag;
-- Read a tag of the form "".
for Object'Class'Tag_Read use Read;
end Class;
package body Class is
procedure Write (S : access Ada.Streams.Root_Stream_Type'Class;
O : Object) is
begin
String'Write (S, "");
String'Write (S, Integer'Image (O.V));
String'Write (S, "");
String'Write (S, "");
end Write;
procedure Write (S : access Ada.Streams.Root_Stream_Type'Class;
Tag : Ada.Tags.Tag) is
-- Write a tag of the form "".
begin
Character'Write (S, '<');
String'Write (S, Ada.Tags.External_Tag (Tag));
Character'Write (S, '>');
end Write;
function Read (S : access Ada.Streams.Root_Stream_Type'Class)
return Ada.Tags.Tag is
-- Read a tag of the form "".
Input : String (1..20);
Input_Len : Natural := 0;
begin
Character'Read (S, Input(1));
if Input(1) /= '<' then
raise Ada.Tags.Tag_Error;
end if;
Input_Len := 0;
for I in Input'range loop
Input_Len := I;
Character'Read (S, Input(I));
exit when Input(I) = '>';
end loop;
if Input(Input_Len) /= '>' or else -- Never found closing character
Input_Len <= 1 then -- Empty tag
raise Ada.Tags.Tag_Error;
end if;
return Ada.Tags.Internal_Tag (Input(1..Input_Len));
end Read;
end Class;
We now have the following output:
which is what we need to write XML.
!ACATS test
A C-Test should be constructed to test this feature.
!appendix
From: Pascal Obry
Sent: Wednesday, January 31, 2001 3:00 PM
Hello,
I have a comment/question about an Ada feature. The current implementation
for S'Class'Output seems to be not flexible enough for some job. Let's look at
the definition for 'Class'Ouput:
<<
29. S'Class'Output
S'Class'Output denotes a procedure with the following
specification:
30. procedure S'Class'Output(
Stream : access Ada.Streams.Root_Stream_Type'Class;
Item : in T'Class)
31. First writes the external tag of Item to Stream (by calling
String'Output(Tags.External_Tag(Item'Tag) - *Note 3.9::.) and
then dispatches to the subprogram denoted by the Output
attribute of the specific type identified by the tag.
>>
The problem here is the way the tag is written using String'Output.
My goal was to write (on disk using streams) objects in XML. But this is not
possible because the tag is written with the string's bounds (String'Output) !
So there is some binary data dumped into the XML file !!!
Of course it is possible to write the tag myself, but then it will be just
impossible to read back the object from the disk...
To summarize:
It is possible to stream an object to disk only in binary format.
It would be nice to be able to control the way the tag is written. I don't
have a proposal for this right now...
I have talked about this issue with my ACT support and we came to the
conclusion that it is just impossible to do what I wanted!
Please let me know if this is clear enough or if you need some more info.
****************************************************************
From: Tucker Taft
Sent: Wednesday, January 31, 2001 5:40 PM
It is definitely frustrating that the dispatching performed by
S'Class'Input cannot be duplicated by "normal" user code. Essentially
one wants to be able to dispatch using an (internal) tag rather
than an object. Alternatively, one wants to be able to construct
an object with default initialization given a tag.
Some kind of "magic" generic might be defined to provide this
capability, or something similar. This definitely will require
some head-scratching.
****************************************************************
From: Pascal Obry
Sent: Saturday, March 17, 2001 2:57 PM
Hello,
Please have a look at this first proposal. I have tried to write something but
this is the very first time I do something like that. I'm not sure the wording
is correct, not talking about my english :) Can you told me if this all this
makes sense to you or if it is just not the right way to approach the
problem. I have tried to follow current AI structure.
<<
!summary
The stream input/output attributes are not flexible enough. The problem is for
the tag handling of tagged object. With the current implementation it is not
possible for example to redefine the 'Read and 'Write to have the object
streamed as an XML object. We propose a solution to solve this problem.
!problem
The problem is for the tag handling of tagged object. With the current
implementation it is not possible for example to redefine the 'Read and 'Write
to have the object streamed as an XML object. Indeed the tag is defined as a
string. It is possible to change the tag string value by using Ada.Tags
facilities. But the tag will always be a string : an unbounded object. Each
time the tagged object will be streamed the tag string value will be written
preceded by the string bound which are binary data. There is no way to avoid
these binary data to be sent. In other word, there is no way to stream an
object with a plain text representation.
!proposal
Have the possibility to set the way the 'Class'Output and 'Class'Input handle
the tag with the new attribute 'Tag_Format. Values for this attributes is of
type Ada.Tags.Tag_Output:
type Tag_Ouput is (Constrained, Unconstrained);
'Tag_Format
is set to Unconstrained by default. This is the current behavior. In this
mode 'Class'Output will call String'Output on the tag string to output the
tag. And 'Class'Input will call String'Input to read the tag. So with
Unconstrained the tag's bounds are used. If it is set to Constrained then
the tag will be written using String'Write (and read using String'Read). In
this case the tag's bounds are not used.
The definition for S'Class'Input and S'Class'Output must be changed
accordingly.
!discussion
We could find some other ways to implement this feature. This is certainly the
lightest one as it requires only one new attributes on tagged record. Another
solution would have been to define two new attributes 'Read_Tag and 'Write_Tag
for reading and writing a tag value. These attributes would have the current
default and could have been overridden by users. It would have been possible to
solve the problem described here with this mechanism. Something like:
procedure Write_Tag (S : access Streams.Root_Stream_Type'Class; O : Object);
for Object'Write_Tag use Write_Tag;
procedure Read_Tag (S : access Streams.Root_Stream_Type'Class; O : Object);
for Object'Read_Tag use Read_Tag;
!example
Here is an example that show the problem. Let's say that we want to stream an
object with an XML encoding:
with Ada.Streams;
package Class is
use Ada;
type Object is tagged record
V : Integer := 2;
end record;
procedure Write (S : access Streams.Root_Stream_Type'Class; O : Object);
for Object'Write use Write;
for Object'External_Tag use "");
end Write;
end Class;
with Ada.Text_IO.Text_Streams;
with Class;
procedure Main is
use Ada;
O : Class.Object;
begin
Class.Object'Class'Output
(Text_IO.Text_Streams.Stream (Text_IO.Current_Output), O);
end Main;
This program output will be something like:
^A^@^@^@^H^@^@^@
The 8 first characters are binary representation for the tag bound. This is no
way to bypass that, so there is no way to stream an object with an XML
representation.
Using the new 'Tag_Format attributes it is possible to control the tag
output. By changing the Class spec to read:
with Ada.Streams;
package Class is
use Ada;
type Object is tagged record
V : Integer := 2;
end record;
procedure Write (S : access Streams.Root_Stream_Type'Class; O : Object);
for Object'Write use Write;
for Object'External_Tag use "