Version 1.10 of ais/ai-00260.txt

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

!standard 13.13.2 (28)          02-05-10 AI95-00260/04
!standard 13.13.2 (31)
!standard 13.13.2 (34)
!standard 13.13.2 (38)
!class amendment 01-02-13
!status Amendment 200Y 02-05-10
!status ARG Approved 7-0-0 02-02-11
!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 introduced 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, which is 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 sent. In other words, there is no way to stream an object with a plain text representation.
!proposal
Two new operational attributes, S'Class'Tag_Read and S'Class'Tag_Write, are defined for reading and writing tags. These attributes are used in the default implementation of S'Class'Input and S'Class'Output.
See wording for the detailed proposal.
!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.2(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.2(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'Class'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 a derived type with parent 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)).
Replace 13.13.2(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.
Add after 13.13.2(38):
User-specified Tag_Read and Tag_Write attributes should raise an exception if presented with a tag value not in S'Class.
!example
Here is an example that illustrates the problem. Suppose 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 specification 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'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 "<External_Tag>". 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, "<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.
!corrigendum 13.13.2(28)
Insert after the paragraph:
For every subtype S'Class of a class-wide type T'Class:
the new paragraphs:
S'Class'Tag_Write
S'Class'Tag_Write denotes a procedure with the following specification:
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
S'Class'Tag_Read denotes a function with the following specification:
function S'Class'Tag_Write ( Stream : access Streams.Root_Stream_Type'Class) return Ada.Tags.Tag;
S'Class'Tag_Read reads a tag from Stream, and returns its value.
The default implementations of the Tag_Write and Tag_Read operate as follows:
!corrigendum 13.13.2(31)
Replace the paragraph:
First writes the external tag of Item to Stream (by calling String'Output(Tags.External_Tag(Item'Tag) -- see 3.9) and then dispatches to the subprogram denoted by the Output attribute of the specific type identified by the tag.
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.
!corrigendum 13.13.2(34)
Replace the paragraph:
First reads the external tag from Stream and determines the corresponding internal tag (by calling Tags.Internal_Tag(String'Input(Stream)) -- see 3.9) and then dispatches to the subprogram denoted by the Input attribute of the specific type identified by the internal tag; returns that result.
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.
!corrigendum 13.13.2(38)
Insert after the paragraph:
User-specified attributes of S'Class are not inherited by other class-wide types descended from S.
the new paragraph:
User-specified Tag_Read and Tag_Write attributes should raise an exception if presented with a tag value not in S'Class.
!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 ?

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

From: Pascal Obry
Sent: Wednesday, June 06, 2001 3:54 AM

Randy Brukardt writes:
 > Attached is the AI that resulted from your proposal and the discussion about
 > it at the Leuven ARG meeting. (Note that this discussion was for intent
 > only, so the proposal is a draft only.) Any comments are welcome.

I had a look and it seems fine with me. The feature I was missing is indeed
available with this proposal. The wording is quite better than mine :)

I have only one question, why choose 'Tag_Write and 'Tag_Read instead of
'External_Tag'Read and 'External_Tag'Write. Here we are talking about external
tag. The way to change the external tag represenation is to use External_Tag
attribute. It seems quite straitforward to just support two new attributes on
External_Tag to have finer control on the external tag representation.

Of course this is a minor issue and I will be able to live with Tag_Write and
Tag_Read.

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

From: Randy Brukardt
Sent: Wednesday, June 06, 2001 6:49 PM

> I had a look and it seems fine with me. The feature I was missing is indeed
> available with this proposal. The wording is quite better than mine :)

Thanks for the complement.

> I have only one question, why choose 'Tag_Write and 'Tag_Read instead of
> 'External_Tag'Read and 'External_Tag'Write. Here we are talking about
> external tag. The way to change the external tag representation is to use
> External_Tag attribute. It seems quite straitforward to just support two
> new attributes on External_Tag to have finer control on the external tag
> representation.

Two reasons: 'External_Tag is a value, not a type. It seems very strange to
associate a subprogram with a value. Secondly, this would require a new
mechanism in compilers: it would be necessary to recognize two attributes at
once when implementing an attribute definition clause. Currently, compilers
simply look at the trailing attribute (which, in most implementations, is at
the top of the tree), and then handle the prefix appropriately. While
'Tag_Read and 'Tag_Write use the same mechanism as for the other stream
attributes, which is well understood.

Note that there is nothing preventing implementors from implementing this
tomorrow (because they are allowed to define implementation-defined
attributes).

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

From: Pascal Leroy
Sent: Thursday, June 06, 2001 2:57 AM

On the one hand I agree that somehow 'External_Tag'Read looks nicer than
'Tag_Read.

On the other hand if you look at the syntax for attribute definition clauses
(RM95 13.1(3, 5)) you see that it would be entirely new syntax.  Therefore,
it would require sizeable changes in the compilers for purely aesthetic
reasons.  Not worth it.  It's much simpler to come up with new attributes.

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

From: Pascal Obry
Sent: Thursday, June 07, 2001 1:03 PM

Right.

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

From: Pascal Obry
Sent: Tuesday, July 17, 2001 4:09 PM

After thinking about this issue we have already S'Class'Input, S'Class'Output,
S'Class'Read and S'Class'Write.

'Class being an attribute and 'Output, 'Input, 'Read 'Write too.

So 'External_Tag'Read is not that different, is it ?

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

From: Randy Brukardt
Sent: Tuesday, July 17, 2001 4:55 PM

'Class (and 'Base) is a special case throughout Ada compilers. It represents
a type, so the only place that it is necessary to handle this is for types.
Essentially, S'Class names a type, just as an identifier does.

'External_Tag is a value.

In the Janus/Ada compiler, the handling of values is completely separate
from the handling of type names. To allow nesting of values in this way in
the attribute definition clause would require a lot of extra code.

In any case, I've now concluded that this very special case solution is the
wrong way to handle the problem. The REAL problem is that it is impossible
to usefully write a custom version of S'Class'Read. That's because there is
no way to dispatch on a tag that you've read in.

If THAT problem was solved with an appropriate building block, we would have
a much more general solution that would be useful for a lot more than
solving a single problem.

Therefore, I've withdrawn my support for this solution, at least until we've
properly investigated methods for dispatching on a tag value.

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

From: Robert Dewar
Sent: Tuesday, July 17, 2001 9:47 PM

This also means that to implement S'Class'Read you have to do very peculiar
things in any Ada compiler, since you cannot represent this as lower level
Ada operations.

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

From: Tucker Taft
Sent: Tuesday, July 17, 2001 5:56 PM

I think this is throwing the baby out with the bathwater.
There is no real harm in keeping the tag_read and tag_write
operational attributes.  They provide a useful capability in
a straightforward fashion.

The building block solution we would all love has been investigated
before (probably several times) and I am not optimistic we will
find an elegant solution in our lifetime.

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

From: Randy Brukardt
Sent: Tuesday, July 17, 2001 6:27 PM

I'm not convinced that the solution has to be that elegant, just functional.
And in any case, I don't see (or remember, for that matter) much of a serious
effort to solve this problem. (The parent type issue, yes. Dispatching control,
no.) I've only seen impossible suggestions involving runtime types, which will
never work in the Ada model. Is there an LSN or other record of the MRT's work
on this issue?

I proposed a solution last week ('Convert), which has been met with the typical
silence. I proposed a solution using syntax a month ago, which was dismissed as
"ugly" -- I knew the syntax itself was no good, but no one suggested an
alternative.

All I want to see is an effort to solve the problem. If there is no good
solution, then so be it. But right now, I see a kludgy solution to a very
specific problem, when a solution with a similar level of kludginess (and
similar or even less implementation effort) appears to solve a bunch of
problems. I'd rather use the more general kludge than the specific one...

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

From: Pascal Obry
Sent: Wednesday, July 18, 2001 1:26 AM

Randy Brukardt writes:
 > 'Class (and 'Base) is a special case throughout Ada compilers. It represents
 > a type, so the only place that it is necessary to handle this is for types.
 > Essentially, S'Class names a type, just as an identifier does.
 >
 > 'External_Tag is a value.

Ok, I see. So let's keep current proposal. It is ok to me.

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

From: Christoph Grein
Sent: Wednesday, July 18, 2001 12:03 AM

I cannot remember having received such a proposal ('Convert). There was silence
on this problem last week. Could you please repeat this proposal?

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

From: Randy Brukardt
Sent: Wednesday, July 18, 2001 2:47 PM

I sent two messages on Friday. A check of the mail server logs shows that
they all were delivered, so I won't repeat the entire message here (I'll
send the entire message to Christoph).

The proposal was buried in a longer message, so people might have missed it:
(note that I'm not excited about the name "Convert"; perhaps someone has a
better one?)

---

...

The real goal here is to get a dynamic view conversion where the type is
identified by a tag value, rather than by name. Going further than that
brings in a truckload of worms. Perhaps we just need to define such an
attribute:

    T'Class'Convert (Obj : in out T'Class; Tag : in Ada.Tags.Tag)

"Returns a view conversion of Obj to the type identified by tag. This
attribute is considered dynamically tagged for dispatching purposes. Raises
Constraint_Error if Tag does not identify a type in T'Class, or if the tag
of Obj does not identify a type that is covered by or descended from the
classwide type identified by Tag (this is 4.6(42)). Note: The tag of <Obj>
is unchanged, as in all view conversions."

...

My preference for 'Parent_Tag is that it is illegal on non-derived,
non-tagged types. But making it illegal on non-derived type probably is a
contract model problem. So, I suppose it probably ought to return Null_Tag
on non-derived types (which means we'd have to define that, too). Similarly,
it ought to return the parent tag even for private types (skipping levels of
derivation would cause problems in practice, and would make it worthless for
the "calling the parent" problem. That means the parent tag of the full
type, which should not be a problem to do (this is not a static expression,
and certainly at runtime the parent tag is known. Indeed, Ada
implementations have to store this in the tag already in order to be able to
make the type conversion checks of 4.6(42) - I would expect that this is
precisely what would be returned. If the compiler has the information, why
shouldn't the programmer be able to use it??)

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

From: Tucker Taft
Sent: Wednesday, July 18, 2001 4:55 PM

Well, I remember seeing this, but I will admit I couldn't figure out
what you meant by a view conversion to a type determined by a tag.

A view conversion has no run-time effect, it
only has a compile-time effect.  Tags, on the other hand,
exist at run-time.  So what are the compile-time and run-time
effects of doing the "view conversion"?  Is it really that you
want to dispatch to something other than the tag of the controlling
object?  That is the controlling tag is not the same as the tag of
the controlling object?

If so, I'm not sure you clarify things by calling it a view conversion.

Can you show how the "view" conversion (or is it tag
substitution?) would be used in a larger example?

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

From: Randy Brukardt
Sent: Wednesday, July 18, 2001 4:55 PM

> ... Is it really that you
> want to dispatch to something other than the
> tag of the controlling object?  That is the controlling
> tag is not the same as the tag of the controlling object?

Yes, that is correct.

> If so, I'm not sure you clarify things by calling
> it a view conversion.

I agree, I'd much rather have a syntax solution for this problem. But everyone
here said that was too heavy, so I'm casting about for other ways to do it.

> Can you show how the "view" conversion (or is it tag
> substitution?) would be used in a larger example?

Sure. Consider the problem of calling the parent operation without naming the
type:

    My_Operation (Root'Class'Convert (Obj, Obj'Parent_Tag), ...);

This would make a dispatching call to the My_Operation for Obj'Parent_Tag. If
the parent type of Obj was Parent, this would be equivalent to the statically
bound call of:

    My_Operation (Parent (Obj), ...);

A compiler could optimize the code to static binding if the tag in a 'Convert
was statically known, but I don't think we want to think about including that
in the semantics.

Similarly, the problem posed by Christoph could be solved by:

    function Closest_Equal (Left, Right : in S'Class) return Boolean is
        GCA : Ada.Tags.Tag :=
              Figure_Greatest_Common_Ancestor (Left'Tag, Right'Tag);
    begin
        return "=" (S'Class'Convert (Left, GCA), S'Class'Convert (Right, GCA));
    end Closest_Equal;

which would test the equality of the common parts (whatever those are) of Left
and Right.

---

The reason for making this a funny "view conversion" is to get a way to inject
the proper tag without inventing any new syntax or semantics (other than for
the attribute itself). After all, a view conversion is precisely how we do
that in Ada 95. The only odd thing here is the tag that 'Convert provides for
dynamic dispatching purposes.

---

Unfortunately, this doesn't work on a dispatching function call. For that, we'd
need another attribute, more magic than this one:

    S'Class'Give_Tag
         function (Obj : in S'Class; Tag : in Ada.Tags.Tag) return S'Class;

which would give the "Obj" parameter "Tag" for dispatching purposes. "Obj" is
then returned as the result of the attribute. This would be used in something
like:

    return S'Class'Give_Tag (S'Input (Stream), Tag_I_Read);

---

More thought appears needed here. Perhaps there is a better way to describe
these attributes.

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

From: Gary Dismukes
Sent: Wednesday, July 18, 2001 8:23 PM

> The reason for making this a funny "view conversion" is to get a way to
> inject the proper tag without inventing any new syntax or semantics (other
> than for the attribute itself). After all, a view conversion is precisely
> how we do that in Ada 95. The only odd thing here is the tag that 'Convert
> provides for dynamic dispatching purposes.

The problem is that you need to make a copy of the value in order
to insert a new tag (assuming the conventional implementation of
tagged objects), and that's just not consistent with the semantics
of a view conversion.  I guess you're thinking in terms of using
this funny conversion only in special contexts, specifically on
dispatching calls, but presumably these conversions would have
to be allowed in other contexts as well (e.g., return statements,
as well as noncontrolling parameters), and there has to be
well-defined semantics for the conversion, which seems to imply
a copy of the object for purposes of substituting the proper tag.
Besides not fitting with the notion of view conversions, that
simply won't work in general as far as I can see.  For example
consider the case of return-by-reference objects, where you can't
ever make a copy.

> Unfortunately, this doesn't work on a dispatching function call. For that,
> we'd need another attribute, more magic than this one:

Right, this just get messier.

> More thought appears needed here. Perhaps there is a better way to describe
> these attributes.

Yes, I think that the idea of describing these as view conversions
certainly has to be abandoned.

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

From: Randy Brukardt
Sent: Wednesday, July 18, 2001 10:17 PM

> The problem is that you need to make a copy of the value in order
> to insert a new tag (assuming the conventional implementation of
> tagged objects), and that's just not consistent with the semantics
> of a view conversion.

No, no, no! View conversions DON'T change the tag of the object. They only
change the tag of the object used for determining the subprogram to call.
You have to get the tag used for dispatching from the conversion (in the
static binding case), without reading the object. This just says to use a
different tag for (dynamic) dispatching: the object is unchanged (as in any
view conversion).

Because of tag-indeterminant items, a compiler has to be prepared to get a
dispatching tag from almost anywhere, so it doesn't seem an implementation
hardship to add another case.

>  I guess you're thinking in terms of using
> this funny conversion only in special contexts, specifically on
> dispatching calls, but presumably these conversions would have
> to be allowed in other contexts as well (e.g., return statements,
> as well as noncontrolling parameters), and there has to be
> well-defined semantics for the conversion, which seems to imply
> a copy of the object for purposes of substituting the proper tag.

Absolutely not. This is a no-op (discounting checks and resolution issues)
as is ANY view conversion of a tagged object in a non-dispatching context.
It has no effect on the object: it just returns the object itself.

> Besides not fitting with the notion of view conversions, that
> simply won't work in general as far as I can see.  For example
> consider the case of return-by-reference objects, where you can't
> ever make a copy.

There is no copy, so this is not a problem. Pascal had suggested a non-view
conversion, which definitely does not work because you can't copy the object
in general.

> Yes, I think that the idea of describing these as view conversions
> certainly has to be abandoned.

No, actually, it is better. There IS a way to use this for controlling
dispatching functions. You need a two parameter dispatching helper function.
(One could imagine this also being an attribute of some sort.)

It would have the specification:

    function Identity (Object : in T; Dispatcher : in T) return T;

It would return Object, doing nothing with the second parameter.

Then, a controlling function can be called as follows:

    declare
	S_Obj : S; -- Not really used.
    begin
      return Identity (S'Input (Stream), S'Class'Convert (S_Obj,
Tag_I_Read);
    end;

which will cause the result of S'Input dispatched to the type identified by
"Tag_I_Read" to be returned.

Even if we did define something like 'Give_Tag (because we could get rid of
the unneeded object here), this would give a viable model to describe its
behavior.

Anyway, if you don't like the view conversion model, please suggest an
alternative that doesn't require a complete rewrite of 3.9.2. Or an
alternative solution.

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

From: Randy Brukardt
Sent: Wednesday, July 18, 2001 10:17 PM

BTW, the main reason for describing this attribute as a "view conversion" is
that the object is untouched by the attribute. If you tried to describe the
attribute as a function, you would get a copy, and that doesn't work for the
reasons Gary mentions (and many others as well, such as "Adjust" issues). We
have to describe it as something else, and "view conversion" seems to be the
closest existing thing.

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

From: Pascal Leroy
Sent: Thursday, July 19, 2001 3:15 AM

I understand what you are trying to achieve, but the above statement is
confused.  A view conversion (to a specific subtype) may be used to force
_static_ binding to a particular subprogram.  The tag is not used in this
context, since there is no dispatching.  If you ever force redispatching by
converting to a class-wide subtype, then you call the subprogram designated by
the tag.

Calling your proposed construct a view conversion is therefore not helpful.  As
I understand it, the 'Convert attribute means "regardless of the tag found in
the object, use the tag T for dispatching".  I guess this attribute would have
to be only usable in subprogram calls.

However, making it an attribute seems to complicate things, because it seems to
imply that this attribute returns a value, which is not really true (as you
point out, trying to build a value by copying Obj and inserting tag Tag causes
endless trouble).

So I think that if we want to pursue this idea we do indeed need special
syntax. I would propose something like:

    My_Operation (Obj with Tag, ...);

(Yet another use of the reserved word "with"...)

This syntax would only be usable as an actual parameter, and the tag checks
that you outlined in a previous mail would be performed before the call.

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

From: Tucker Taft
Sent: Thursday, July 19, 2001 9:38 AM

I agree with Pascal here -- at least this is why the term "view conversion"
was confusing me.  What we meant by "view" conversion was something which
changed the compile-time view of an object, and had no effect on its
run-time representation.  I suppose if you stand on your head, you
could say this is similar in that it is saying "use this tag for
dispatching but don't change the run-time representation" but it
is a bit of a stretch.

> ...
> However, making it an attribute seems to complicate things, because it
> seems to imply that this attribute returns a value, which is not really
> true (as you point out, trying to build a value by copying Obj and
> inserting tag Tag causes endless trouble).

I think we could still use an attribute, though it clearly would need
some special semantics.

> So I think that if we want to pursue this idea we do indeed need special
> syntax. I would propose something like:
>
>     My_Operation (Obj with Tag, ...);
>
> (Yet another use of the reserved word "with"...)

I think this could also be Obj'With_Controlling_Tag(Tag)

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

From: Christoph Grein
Sent: Friday, July 20, 2001 12:15 AM

I would propose to use the term "use". "with" seems to imply the object has this
tag, which is misleading - we rather use another tag:

   My_Operation (Obj use Tag, ...);

or

   My_Operation (Obj'Use_Controlling_Tag (Tag), ...);

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

From: Pascal Leroy
Sent: Friday, July 20, 2001 3:15 AM

> I think we could still use an attribute, though it clearly would need
> some special semantics.
>
> I think this could also be Obj'With_Controlling_Tag(Tag)

As you know, an attribute is a name.  But here it would not name anything,
because really there is no such thing as an object Obj having the tag Tag.
So this would require heavy special casing in the language description as
well as in compilers (I hate to think of the number of places in my compiler
where I would have to write "if this is attribute With_Controlling_Tag then
do something weird").

I hate it when the syntax does not reflect what is really going on behind
the scenes.  To me, an attribute seems to imply that some object is created
and used in the dispatching call.  I believe that a new syntax is much
clearer.

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

From: Randy Brukardt
Sent: Friday, July 20, 2001 12:31 PM

The attribute seems to have one advantage over the syntax (at least if you
restrict the syntax only to actual parameters in calls) -- it can be used to
control a function with a controlling result as well.

For instance, to control S'Input:

    return S'Input'With_Controlling_Tag(Tag_Read_Earlier);

OTOH, if we just made the syntax an expression, which acts like a qualified
expression with special effects on controlling parameters and results, then
it too could be used to control functions with controlling results:

    return S'Input with Tag_Read_Earlier;

(An alternative is to revisit my original idea that the syntax is a modifier
for calls: both function and procedure calls:

    return S'Input with Tag_Read_Earlier;

    return Equal (A, B) with Tag_Read_Earlier;

    My_Operation (Obj, ...) with Obj'Parent_Tag;)

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

From: Randy Brukardt
Sent: Thursday, October 18, 2001 5:45 PM

From the minutes of the 14th ARG meeting (Bloomington MN):

Change the title: How to control the tag representation in a stream.

These are operational attributes with all of the effects that this implies.
They have similar inheritance rules as 'Read & 'Write have.

Correct the wording: Formal S => Stream. Item'Tag => Tag

Tucker now states that he thinks that his previous opinion was wrong; these
attributes should be classwide only, without inheritance. Much discussion
ensues. Eventually the group concludes that the attributes make the most sense
only on the classwide type, as they are only used in dispatching operations.

No inheritance would mean that child types don't get it. If T2 is derived from
a type T1 which had a user-defined Tag_Write, T2'Class'Output wouldn't use the
user-defined Tag_Write. This seems wrong. As a solution to this problem, Tucker
suggests that the default implementation should be a call to the Parent'class
operation.

Someone makes a more radical suggestion to disallow overriding of these
attributes. There is not much support for this idea - it might be useful.

In Summary: the attributes are classwide, inherited (by the default operation
calling the parent), no restriction on overriding. Approved this intent: 6-0-3

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


Questions? Ask the ACAA Technical Agent