Version 1.4 of ais/ai-00260.txt

Unformatted version of ais/ai-00260.txt version 1.4
Other versions for file ais/ai-00260.txt

!standard 13.13.2 (31)          01-06-02 AI95-00260/01
!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 replace S'Class'Input with custom versions
!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 formal. 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 specific tagged subtype S, the following attributes are defined.
S'Tag_Write
procedure S'Tag_Write (S : access Streams.Root_Stream_Type'Class;
Tag : Ada.Tags.Tag);
S'Tag_Write writes the value of Tag to Stream.
S'Tag_Read
function S'Tag_Read (S : access Streams.Root_Stream_Type'Class)
return Ada.Tags.Tag;
S'Tag_Read reads a tag from Stream, and returns its value.
The Tag_Write and Tag_Read attributes of the parent type are inherited when a type is derived. 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 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, 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(27):
For every subtype S of a specific tagged type T, the following operational attributes are defined.
S'Tag_Write
procedure S'Tag_Write (S : access Streams.Root_Stream_Type'Class;
Tag : Ada.Tags.Tag);
S'Tag_Write writes the value of Tag to Stream.
S'Tag_Read
function S'Tag_Read (S : access Streams.Root_Stream_Type'Class)
return Ada.Tags.Tag;
S'Tag_Read reads a tag from Stream, and returns its value.
The Tag_Write and Tag_Read attributes of the parent type are inherited as specified in 13.1. The default implementation of Tag_Write is to call String'Output(Stream, Tags.External_Tag(Item'Tag)) -- see 3.9. 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.
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 "<object>";
end Class;
package body Class is
procedure Write (S : access Ada.Streams.Root_Stream_Type'Class; O : Object) is begin String'Write (S, "<v>"); String'Write (S, Integer'Image (O.V)); String'Write (S, "</v>"); String'Write (S, "</object>"); 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^@^@^@<object><v> 2</v></object>
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 "<External_Tag>". for Object'Tag_Write use Write;
function Read (S : access Ada.Streams.Root_Stream_Type'Class) return Ada.Tags.Tag; -- Read a tag of the form "<External_Tag>". for Object'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, "<v>"); String'Write (S, Integer'Image (O.V)); String'Write (S, "</v>"); String'Write (S, "</object>"); end Write;
procedure Write (S : access Ada.Streams.Root_Stream_Type'Class; Tag : Ada.Tags.Tag) is -- Write a tag of the form "<External_Tag>". 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 "<External_Tag>". 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:
<object><v> 2</v></object>
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 "<object>";

end Class;

package body Class is

   procedure Write (S : access Streams.Root_Stream_Type'Class; O : Object) is
   begin
      String'Write (S, "<v>");
      String'Write (S, Integer'Image (O.V));
      String'Write (S, "</v>");
      String'Write (S, "</object>");
   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^@^@^@<object><v> 2</v></object>

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 "<object>";
   for Object'Tag_Format use Constrained;

end Class;

We now have the following output:

   <object><v> 2</v></object>
>>

I hope to hear from you soon,
Pascal.

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

From: Pascal Obry
Sent: Sunday, March 18, 2001 3:19 PM

Please ignore my proposal as it is not working for reading the Tag. When
reading an object (by calling 'Class'Input) the Tag is not known so the string
bounds are needed !

I'll try to find something else...

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

From: Randy Brukardt
Sent: Monday, March 19, 2001 5:20 PM

Too bad. We have an (empty) AI on this subject just waiting for a proposal.
(It seems like a problem worth solving.) Tucker pointed out that the problem
of being unable to handle the tag is really one of being unable to write a
new implementation of T'Class'Input, and *that* is caused by not having a
way to dispatch based on a (internal) tag value (or, equivalently, being
able to create a default-initialized object from a tag value). We can write
code to read the tag however you need, you can look up the tag with
Internal_Tag, but you can't write the needed dispatching and/or object
creation.

Tuck's musings suggested that perhaps a "magic" generic with this capability
would be the way to go. Perhaps you can find a solution and make a proposal
based on that idea??

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

From: Pascal Obry
Sent: Wednesday, April 04, 2001 11:30 AM

Hello,

Here is a proposal for an Ada stream extenssion, the goal is to provide more
control on the Tag for streamed object.

<<
!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
attribute 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 bounds 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 attributes.

Two new attributes form are proposed:

   S'External_Tag'Read
     This attribute can be used to set the right function to use for reading
     the tag on a stream. This function will be called by S'Class'Input
     instead of the default one. The function prototype must be:

        function Read (S : access Streams.Root_Stream_Type'Class)
          return Ada.Tags.Tag;

   S'External_Tag'Write
     This attribute can be used to set the right function to use for writing
     the tag on a stream. This function will be called by S'Class'Output
     instead of the default one. The procedure prototype must be:

        procedure Write (S : access Streams.Root_Stream_Type'Class;
                         Tag : Ada.Tags.Tag);

The definition for S'Class'Input and S'Class'Output must be changed
accordingly.

!discussion

This proposal does not really introduce new attributes. The S'External_Tag
attribute can be composed with 'Read and 'Write. This change is upward
compatible as it does not change the semantic for streamed object when it is
not used.

!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 "<object>";

end Class;

package body Class is

   procedure Write (S : access Streams.Root_Stream_Type'Class; O : Object) is
   begin
      String'Write (S, "<v>");
      String'Write (S, Integer'Image (O.V));
      String'Write (S, "</v>");
      String'Write (S, "</object>");
   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^@^@^@<object><v> 2</v></object>

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 'External_Tag'Read and 'External_Tag'Write attributes it is
possible to control the tag input/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;

   for Object'External_Tag use "object";

   procedure Write (S : access Streams.Root_Stream_Type'Class; O : Object);
   for Object'Write use Write;

   procedure Write (S : access Streams.Root_Stream_Type'Class;
                    Tag : Ada.Tags.Tag);
   --  Implementation not shown. This function:
   --    1) output character '<'
   --    2) output Ada.Tags.External_Tag (Tag)
   --    3) output character '>'
   for Object'External_Tag'Write use Write;

   function Read (S : access Streams.Root_Stream_Type'Class)
     return Ada.Tags.Tag;
   --  Implementation not shown. This function:
   --    1) read first character, raise Ada.Tags.Tag_Error if it is not '<'
   --    2) read the tag (let's call it Tag_String)
   --    3) check for end of tag which is '>' (raise Ada.Tags.Tag_Error if no
   --       such character on the stream)
   --    4) return Ada.Tags.Internal_Tag (Tag_String)
   for Object'External_Tag'Read use Read;

end Class;

We now have the following output:

   <object><v> 2</v></object>
>>

Let me know what you think about this proposal ? Do you need something else ?

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

Questions? Ask the ACAA Technical Agent