!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 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; 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^@^@^@ 2 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: 2 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 Class; package body Class is procedure Write (S : access 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; 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^@^@^@ 2 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 ""; for Object'Tag_Format use Constrained; end Class; We now have the following output: 2 >> 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 ""; end Class; package body Class is procedure Write (S : access 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; 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^@^@^@ 2 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: 2 >> 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 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 ****************************************************************