!standard 4.6 (00) 01-06-01 AC95-00009/01 !class uninteresting 01-06-01 !status received 01-05-17 !subject Dynamic view conversion !summary !appendix !topic Dynamic view conversion !reference RM95-4.6 !from Michael Gray !sent Thursday, May 17, 2001, 7:23 AM !discussion We had a problem in comparing two tagged records A and B, where the type of B is an extension of the type of A, and the type of A is an extension of some Root_Type. In a general piece of code (i.e. a piece of code written without knowledge of which descendants of Root_Type exist), we needed to compare the values of A and B, where the required semantics were to compare the common fields, i.e. to check whether the fields of A had identical values in B. Although this seems like quite a reasonable thing to want to do, it seems to be impossible within the language. I.e. consider procedure X (A, B : in Root_Type'Class) ... if A = B ... always delivers false if A and B are of different types. The problem here is that there is no syntax that allows an object (B) to be view-converted to the type of another object (A). Something along the lines of A'Class(B) would be needed. This would then allow a comparison A = A'Class(B). This would make inherited operations far more useful. In other words, allow a view conversion where the target view is dynamically determined rather than statically. In implementations terms (I guess), to execute the required comparison, it's 'just' necessary to look up the address of the = operation in the dispatch table (indexed via the tag of A), and to execute it, passing the objects A and B as parameters .... ? **************************************************************** From: Pascal Leroy Sent: Monday, May 28, 2001 5:25 AM I'd say that we don't want to add more dynamicity in the language at this stage. My gut feeling is that defining the proposed construct would be quite hard, and would probably have a significant impact on implementations. But more importantly, conversions never change the tag of an object (RM95 3.9(25)) so I don't see how the proposed construct would help: in the comparison A = A'Class (B), the tags would still be different, and the comparison would still return False. Note that while it is possible to write value conversions for tagged types (RM95 4.6(5/1)) the above rule presumably applies to both view conversions and value conversions. I have occasionally wanted a conversion-that-changes-the-tag, but not often enough that I see this as an important addition to the language. Incidentally, if such a conversion existed I believe that it could be used to solve your problem (in conjunction with clever use of dispatching subprograms). **************************************************************** From: Christop Grein Sent: Tuesday, May 29, 2001 5:22 AM > > I.e. consider > > > > procedure X (A, B : in Root_Type'Class) > > ... > > if A = B ... > > > > always delivers false if A and B are of different types. > > I've also once come into a situation where I wanted to compare the common parts of tagged types, i.e. I would have needed an operation that, given two objects of a class, returned the type of the first common ancestor, which you then could use in a type conversion. Something like T'Common_Ancestor (A, B) converts A (the first argument) to that type which is the nearest common ancestor (going backward) of A and B in the hierachy T (for A, B in T). There must always be a common root (it might however be abstract and this could present a problem): Root / \ T1 T2 /\ /\ T11 T12 T21 T22 A: T11; B: T12; Root'Common_Ancestor (A, B) would convert A to T1 A: T11; B: T2; Root'Common_Ancestor (A, B) would convert A to Root Thus one could write Compare (Left => Root'Common_Ancestor (A, B), Right => Root'Common_Ancestor (B, A)) where Compare is a dispatching operation. [Problem when the common ancestor is abstract and no operation exists => Constraint_Error, Program_Error ?] I have no idea whether and how this could be implemented. I'm even not sure whether such an operation really could make sense in general. The more I think about it, the more it seems to be a burden for implementors. So I returned to plain discriminated records. **************************************************************** From: Michael Gray Sent: Thursday, May 31, 2001 10:53 AM > I'd say that we don't want to add more dynamicity in the language at this > stage. My gut feeling is that defining the proposed construct would > be quite hard, and would probably have a significant impact on > implementations. Fair enough. This problem hasn't cropped up for us very frequently; the email from Christoph Grein ("I've also once come into a situation where I wanted to compare the common parts of tagged types, ... " also implies that it's an occasional problem rather than a recurrent one. **************************************************************** From: Randy Brukardt Sent: Wednesday, June 06, 2001 7:31 PM I was thinking about this again when reviewing AI-260. The problem in AI-260 is solved with a rather specific feature. The dynamic view conversion problem was solved with dynamic view conversions. But the problem in both cases isn't conversion, or reading tags, its really controlling dispatching. A solution that included more control over dispatching would probably solve both problems. Pascal Leroy said: > I'd say that we don't want to add more dynamicity in the language at this > stage. My gut feeling is that defining the proposed construct would be quite > hard, and would probably have a significant impact on implementations. That might be true for the exact proposal given, but I think that controlling dispatching isn't likely to cause problems. Consider that pseudo-code for the canonical implementation of a dispatching call looks like: declare Disp_Tag : Ada.Tags.Tag := 'Tag; begin for I in loop if I'Tag /= Disp_Tag then raise Constraint_Error; end if; end loop; Disp_Tab (Slot_Number).all (); end; What we need to solve both this problem and the AI-260 one is a way to dispatch on a user-calculated tag. Such a call would probably have pseudo code that looks like: declare Disp_Tag : Ada.Tags.Tag := ; begin for I in loop if I'Tag not in Disp_Tag'Class then raise Constraint_Error; end if; end loop; Disp_Tab (Slot_Number).all (); end; The parameter check in this case is essentially the same as that done for type conversions, so compilers certainly know how to deal with it. This suggests that there isn't much of a problem with this as a (runtime) model. (Tag indeterminate operands would just use the calculated tag, which is exactly how they are handled in the current model of dispatching). The problem here is to think of some appropriate syntax for this. Tucker had suggested a magic generic, but I can't figure out how that would work. I also thought about using a variation of the proposed Obj.Op(...) calling to handle this, but that doesn't give us an appropriate way to handle Op. So, the best I can come up with is a new syntax: ::= : ::= (and so on for the other forms of call). We'd also want to add a routine to Ada.Tags: function Common_Ancestor (A, B : in Ada.Tags.Tag) return Tag; -- Returns the common ancestor closest to A and B. Raises Tag_Error -- if A and B do not have a common ancestor. Then, to solve Michael Grey's problem: procedure Common_Parts_Equal (A, B : in Root_Type'Class) is Ancestor_Tag : Ada.Tags.Tag := Ada.Tags.Common_Ancestor (A'Tag, B'Tag); -- This can't raise Tag_Error, as the common ancestor must be -- at least Root_Type. begin return Ancestor_Tag : "=" (A, B); -- This would call the appropriate "=" for Ancestor tag, both -- operands must therefore have types that are descendants of the -- type identified by Ancestor tag. end Common_Parts_Equal; Similarly, to solve the AI-260 problem, we'd override XML'Class'Input with function XML_Object_Input (Stream : access Ada.Streams.Root_Stream_Type'Class; return XML'Class is Object_Tag : Ada.Tags.Tag := Read (Stream); -- Read from AI-260 example. begin return Object_Tag : XML'Input (Stream); -- Which would call the 'Input routine for the type identified by -- object tag. end XML_Object_Input; The only real problem I see with this is the need for new syntax. Perhaps someone else can figure a way around that?? (Or a better syntax.) **************************************************************** From: Pascal Leroy Sent: Tuesday, June 12, 2001 3:17 AM While I have some sympathy for supporting the kind of dispatching that Randy and Michael advocate, I am adamantly opposed to introducing newly (and disgraceful) syntax for that purpose. As I mentioned earlier, all you need is a conversion that changes the tag, but leaves the rest of the record unchanged. This can be achieved by a new predefined generic (which could go in Ada.Tags): generic type Destination is tagged private; type Source is abstract new Destination with private; function Tag_Conversion (S : Source; T : Ada.Tags.Tag) return Destination'Class; The effect of which is to make a copy of S, replacing the tag with T. Then the result of calling (an instantiation of) this function may be used for dispatching, and the right thing will happen. > We'd also want to add a routine to Ada.Tags: > > function Common_Ancestor (A, B : in Ada.Tags.Tag) return Tag; > -- Returns the common ancestor closest to A and B. Raises Tag_Error > -- if A and B do not have a common ancestor. Again, I object to Common_Ancestor, which represents one usage pattern among many others. I would be willing to add to Ada.Tags a function to return the tag of the parent: function Parent_Tag (T : Ada.Tags.Tag) return Ada.Tag.Tags; Using this function, it would be straightforward to implement Common_Ancestor by walking up the derivation tree. It would also be possible to implement other usage patterns. And finally it would be easier to call the parent in general-purpose code. **************************************************************** From: Randy Brukardt Sent: Tuesday, June 12, 2001 4:52 PM > The effect of which is to make a copy of S, replacing the tag with T. Then the > result of calling (an instantiation of) this function may be used for > dispatching, and the right thing will happen. Danger Will Robinson!!!! When you say "copy", you immediately have two problems: (1) Copying the item would trigger all of the Adjust rules. But which version of Adjust do you call? And if you don't call Adjust, then you have an object for which the invariants don't hold. For instance, Claw Window objects are linked into an active object list. If that doesn't happen, you'll have problems, especially if the user passes this result into something that closes the object. (2) Once you say copy, you've eliminated the possibility to use this on limited types. That seems like an unacceptable limitation; at least half of the O-O types I've used are limited. (Once you've given up and gone over to the dark side of reference semantics, it usually is best to prevent all copying of objects.) I assume that you are restricting this somehow to downward conversions, because clearly we can't allow upward conversions. That would have to be done at runtime, since the check couldn't be part of a generic. The problem is exactly one of controlling dispatching; to try to describe it as something else is likely to bring in additional, unnecessary problems (such as those above). Note that in my scheme, if the called routine wants to force redispatching, it will redispatch to the original tag (as normally happens in Ada 95); while in yours, the called routine will redispatch to the new tag (which clearly would be different than normal practice). In any case, I suggested what I did to try to start some discussion in this topic. Since only you, I, and Michael have commented on it, it obviously isn't of interest to many. And since my solution is too heavy for some, your solution does not work, and Tucker hasn't weighed in with the solution to all problems :-), it probably is best to forget this one altogether. > ... I would be willing to add to Ada.Tags a function to return the > tag of the parent: > > function Parent_Tag (T : Ada.Tags.Tag) return Ada.Tag.Tags; > > Using this function, it would be straightforward to implement Common_Ancestor > by walking up the derivation tree. It would also be possible to implement > other usage patterns. This seems fine. (Although I can't think of any way to implement Common_Ancestor using it -- but since any implementation of Common_Ancestor would have to use a parent function of some sort, that's not a problem. The problem is that walking up the tree alone isn't enough, because you don't know which operand has to be walked.) > And finally it would be easier to call the parent in > general-purpose code. This seems to be true only if you have syntax to control the dispatching: Ada.Tags.Parent_Tag(Object):Me(Object, Args...); If you have to instantiate a generic to do it, it doesn't help anything at all. That would be twice as much text as the type conversion that we need now. (Unless, of course, we add "automatic" instantiations to the language, an idea which I would oppose on the grounds that "implicit" declarations are nearly always bad.) **************************************************************** From: Christoph Grein Sent: Monday, June 18, 2001 4:55 AM > This seems fine. (Although I can't think of any way to implement > Common_Ancestor using it -- but since any implementation of Common_Ancestor > would have to use a parent function of some sort, that's not a problem. The > problem is that walking up the tree alone isn't enough, because you don't > know which operand has to be walked.) > A further operation in Ada.Tags would be needed like function is_Derived_From (Left, Right: Ada.Tags.Tag) return Boolean; > > And finally it would be easier to call the parent in > > general-purpose code. > > This seems to be true only if you have syntax to control the dispatching: > > Ada.Tags.Parent_Tag(Object):Me(Object, Args...); > > If you have to instantiate a generic to do it, it doesn't help anything at > all. That would be twice as much text as the type conversion that we need > now. (Unless, of course, we add "automatic" instantiations to the language, > an idea which I would oppose on the grounds that "implicit" declarations are > nearly always bad.) I think this new syntax proposal in its utmost ugliness has no chance in the eyes of the Ada-powers-that-be. Are there similar functionalities in Java (IIRC no) or C++ (I don't know)? The problem I tried to solve was that with discriminated types, an operation like Compare (Left, Right) can have Left and Right with different discriminants and I can easily compare the common parts, but a dispatching operation cannot have different tags on Left and Right, and I have no access to the common parts. It seems the problem is a mixture of conversion and dispatching control. **************************************************************** From: Randy Brukardt Sent: Tuesday, July 3, 2001 4:14 PM > > > And finally it would be easier to call the parent in > > > general-purpose code. > > > > This seems to be true only if you have syntax to control the dispatching: > > > > Ada.Tags.Parent_Tag(Object):Me(Object, Args...); > > > > If you have to instantiate a generic to do it, it doesn't help anything at > > all. That would be twice as much text as the type conversion that we need > > now. (Unless, of course, we add "automatic" instantiations to the language, > > an idea which I would oppose on the grounds that "implicit" declarations are > > nearly always bad.) > > I think this new syntax proposal in its utmost ugliness has no chance in the > eyes of the Ada-powers-that-be. It wasn't so much of a serious proposal, but rather an attempt to put the problem (and one way to solve it) in front of the community. > Are there similar functionalities in Java (IIRC no) or C++ (I don't know)? Me either. > The problem I tried to solve was that with discriminated > types, an operation like > > Compare (Left, Right) > > can have Left and Right with different discriminants and I can easily compare > the common parts, but a dispatching operation cannot have different tags on Left > and Right, and I have no access to the common parts. > > It seems the problem is a mixture of conversion and dispatching control. No, that's the thinking that leads to the paralysis of being unable to do anything about it. In order to solve this, we have to think a bit outside of the box. In Ada 95, conversions are used to control dispatching. So one can think of this problem as one of dispatching control OR conversion. But both is not necessary. If we found a way to control dispatching directly, then the existing (implicit) conversions present in Ada 95 is sufficient to provide all of the conversions needed. An alternative way to look at the problem is purely as an exercise in view conversions. If we know the common type that we want to compare, we simply would convert to that type explicitly (this works in Ada 95, and indeed is how I approached the problem): Compare (Base_Type(Left), Base_Type(Right)); which will call the version of Compare for Base_Type. So an alternative solution would look something like: declare Common_Tag : Ada.Tags.Tag := Common_Ancestor(Left'Tag, Right'Tag); -- Common_Ancestor doesn't have to be predefined. begin Compare (Magic_Conversion (Left, Common_Tag), Magic_Conversion (Right, Common_Tag)); end; The problem is defining Magic_Conversion. One could consider a (magic) generic for this purpose: generic type Some_Tagged_Classwide (<>) is tagged limited private; function Magic_Conversion (Obj : in Some_Tagged_Classwide; New_Tag : in Ada.Tags.Tag) return Some_Tagged_Classwide; which would then be instantiated: function My_Magic_Conversion is new Magic_Conversion (Root_Type'Class); The trouble with this is that this would give us a value conversion. But for this purpose (where we don't actually want to convert anything, we're just using that as a shorthand to control dispatching), we really want a view conversion. Moreover, we don't want to try to copy anything, as that triggers Adjust, and doesn't work for limited types. I suppose we could define this generic to be totally magic, doing a view conversion, but I think I'd rather have syntax in that case. Note that Pascal's previous comment saying that you need to change the tag is wrong. You NEVER change the tag in Ada 95, you simply change the tag that is used for dispatching determination. All we need here is to be able to control what tag is used for dispatching; we don't need to change the object in any way. One reason that I looked at syntax for this is the well-known problem with calling the parent routine in an overriding of a operation: My_Operation (Parent_Type(Object), ....); which is fragile because we have to explicitly write the parent type's name. If an additional type is inserted between the child and parent types, all of the uses of Parent_Type need to be searched out and changed if they are intended to point at the parent. One use for the proposed syntax would be to force dispatching to the parent without naming it: Ada.Tags.Parent_Tag (Object'Tag):My_Operation (Object, ...); We could simplify this further by introducing a 'Parent_Tag attribute: Object'Parent_Tag:My_Operation (Object, ...); --- Anyway, my syntax proposal was really intended to start a discussion. It solves three problems that have been discussed in various amounts: 1) The AI-260 problem for which the band-aid of 'Tag_Read and 'Tag_Write attributes were introduced. I'd prefer a more building block approach if possible. 2) The Compare problem noted by several people. 3) The problem that the call to parent operation usually made in overriding operations has to explicitly use the name of the parent type. At this point, since hardly anyone has seemed interested enough to discuss it, much less propose an alternative, I consider this whole discussion dead. **************************************************************** From: Robert Duff Sent: Wednesday, July 4, 2001 10:23 AM Christoph Grein wrote: > Compare (Left, Right) I haven't followed this whole discussion, so this might be a non sequitor, but you can do something like this: function Compare(X, Y: T'Class) return Boolean is begin if X'Tag = Y'Tag then return Compare_Method(X, Y); -- dispatching call else return False; end if; end Compare; **************************************************************** From: Christoph Grein Sent: Wednesday, July 4, 2001 12:25 AM > One reason that I looked at syntax for this is the well-known problem with > calling the parent routine in an overriding of a operation: > > My_Operation (Parent_Type(Object), ....); > > which is fragile because we have to explicitly write the parent type's name. > If an additional type is inserted between the child and parent types, all of > the uses of Parent_Type need to be searched out and changed if they are > intended to point at the parent. I haven't thought of this, but I think this problem deserves a well-designed solution. This is much like the problem of inadvertant overriding or not overriding an inherited operation, which is being solved via pragmas. > One use for the proposed syntax would be to force dispatching to the parent > without naming it: > > Ada.Tags.Parent_Tag (Object'Tag):My_Operation (Object, ...); > > We could simplify this further by introducing a 'Parent_Tag attribute: > > Object'Parent_Tag:My_Operation (Object, ...); Couldn't this be solved by a new attribute T'Parent_Type: My_Operation (T'Parent_Type(Object), ....); without the need of a new syntax? Here for every tagged type T, T'Parent_Type denotes the parent type (or, if T is the root of the hierarchy, denotes T itself). [This is much like T'Class'Class being the same as T'Class.] > Anyway, my syntax proposal was really intended to start a discussion. It > solves three problems that have been discussed in various amounts: > > 1) The AI-260 problem for which the band-aid of 'Tag_Read and 'Tag_Write > attributes were introduced. I'd prefer a more building block approach if > possible. > > 2) The Compare problem noted by several people. > > 3) The problem that the call to parent operation usually made in overriding > operations has to explicitly use the name of the parent type. > > At this point, since hardly anyone has seemed interested enough to discuss > it, much less propose an alternative, I consider this whole discussion dead. I think it should not be dead for the third problem. This is also a problem Java (and I gather many other OO languages) suffer from. A solution would be in the Ada spirit. **************************************************************** From: Steve Deller Sent: Wednesday, July 4, 2001 6:49 PM Are you proposing a new attribute function that takes an object and returns an object with the parent tag of the original object? Or are you proposing an attribute that denotes a subtype (much like T'Class denotes a subtype), and intending type conversion to take care of converting an object? In the latter case, one could also write: x : T ; y : T'Parent_Type ; which would be invalid for the former approach. Somehow the latter seems more appropriate to me. It feels like an appropriate "completion" to the 'Class and 'Tag attributes. I too like the "building block" approach and it does seem that 'Parent_Type would be a powerful building block. The "compare the most common ancestor" is then easy to write, as you point out: function Compare_Common( X, Y : T'Class ) return Boolean is Temp : T'Class ; Tx : T'Class ; Ty : T'Class ; begin loop loop if Tx'Tag = Ty'Tag then return Compare_Method( Tx, Ty ) ; end if ; Temp := T'Parent_Type( Tx ) ; exit when Tx = Temp ; Tx := Temp ; end loop ; Temp := T'Parent_Type(Py) ; exit when Temp'Tag = Ty'Tag ; Ty := Temp ; end loop ; raise Program_Error ; -- no common ancestor found and at least the root T should be found end Compare_Common ; **************************************************************** From: Robert Duff Sent: Thursday, July 5, 2001 9:22 AM > without the need of a new syntax? > Here for every tagged type T, T'Parent_Type denotes the parent type (or, if T is > the root of the hierarchy, denotes T itself). [This is much like T'Class'Class > being the same as T'Class.] A Parent_Type attribute was considered during the 9X design. I don't remember the details, or why it was rejected. The T'Parent_Type = T case at the root seems fishy to me. Some other questions that would have to be answered: A private type does not have a "parent type" -- it has an "ancestor type". Does that mean there should be a 'Ancestor_Type? Or does 'Parent_Type return the ancestor type for a partial view. If the ancestor of a private type is different from the parent of the full type, does 'Parent_Type return something different depending on where it appears in the source code (i.e., whether the full type is visible)? Sounds questionable to me... For a generic formal type, does T'Parent_Type in the instance return something different from T'Parent_Type in the generic? **************************************************************** From: Christoph Grein Sent: Monday, July 9, 2001 2:12 AM > Are you proposing a new attribute function that takes an object and returns > an object with the parent tag of the original object? Or are you proposing > an attribute that denotes a subtype (much like T'Class denotes a subtype), > and intending type conversion to take care of converting an object? In the > latter case, one could also write: > x : T ; > y : T'Parent_Type ; > which would be invalid for the former approach. Yes, this was the idea, but see the comments of Robert Duff about the problems with private types, which only have ancestors. My feeling would be that you "get what you can see", i.e. for private view T'Parent_Type is the ancestor, for the full view is the parent. But I'll leave the discussion to more knowledgable people. ---- The following code [Steve Deller's - ED] would not work since you do not know where X and Y meet in the hierarchy. Also initial values for Tx and Ty are missing, I assume you mean X resp. Y. The declaration of Temp, Tx, Ty is illegal and the code impossible since they must be initialized and are then constrained. **************************************************************** From: Randy Brukardt Sent: Friday, July 13, 2001 5:07 PM > ...Or are you proposing an attribute that denotes a subtype (much like > T'Class denotes a subtype), and intending type conversion to take care > of converting an object? ... > > Somehow the latter seems more appropriate to me. It feels like an > appropriate "completion" to the 'Class and 'Tag attributes. That seems like a truckload of worms to me. (Like a can of worms, only bigger.) You'd have problems with discriminants, private types, generics, and gosh knows what else. > I too like the "building block" approach and it does seem that 'Parent_Type > would be a powerful building block. > > The "compare the most common ancestor" is then easy to write, Perhaps, but not the way you show it below: > as you point out: > > function Compare_Common( X, Y : T'Class ) return Boolean is > Temp : T'Class ; Umm, this is illegal in Ada 95. The item needs to be initialized, and the type can't be changed after the initialization. So you can't solve the problem this way. **************************************************************** From: Michael Yoder Sent: Friday, July 6, 2001 11:06 AM It seems to me the principal missing thing is a way to go from a tag to a type. Given that, everything else can be done by defining the right operations on tags (e.g., a function to yield the tag of the parent given the tag of the type). Here's an initial suggestion: an attribute 'Type that takes a single argument which is a tag, and which yields a tagged type. (Yes, I know this is a peculiar sort of attribute.) The prefix must be a tagged type, and the tag is required to be that of a type in the derivation class of the prefix, or Tag_Error is raised. If this type is allowed to be used generally, e.g. to declare an object, more is needed because of discriminants. Consider the following: declare X : T'Type (Tag); ... We must require that the type corresponding to Tag has defaults for all its discriminants (or an exception is raised). It is possible that T has discriminants which you want to use; consider this: declare X : T'Type (Tag) (Discrim1, Discrim2); ... Here (Discrim1, Discrim2) must be a valid discriminant constraint for the type T. Moreover Tag mustn't correspond to a type that replaces T's discriminants with others, or an exception is raised. I do not have a good handle on how difficult this would be to implement on the compiler side, but it seems to me that the run-time side could work using standard techniques. (If I'm wrong, by all means disabuse me of this notion.) In the above, "an exception" is one of Constraint_Error, Program_Error, or Tag_Error. I lean towards Constraint_Error. Oh, and I'm not seriously suggesting that the name of the attribute be 'Type, though I suppose it could be. **************************************************************** From: Christoph Grein Sent: Monday, July 9, 2001 2:47 AM > declare > X : T'Type (Tag); > ... > > We must require that the type corresponding to Tag has defaults for all its > discriminants (or an exception is raised). And T'Type (Tag) must not be abstract to be consistent with the current situration that there are never objects of an abstarct type. > > It is possible that T has discriminants which you want to use; consider > this: > > declare > X : T'Type (Tag) (Discrim1, Discrim2); > ... > > Here (Discrim1, Discrim2) must be a valid discriminant constraint for the > type T. Moreover Tag mustn't correspond to a type that replaces T's > discriminants with others, or an exception is raised. Uh-hu, where does this lead us? And it seems to me that for the problem of the common ancestor, we would need more for a nice solution: Tags could be partially ordered: function "<=" (Left, Right: Tag) return Boolean; Returns True if Right is in the derivation tree of Left, else False. X: TX; Y: TY; X'Tag <= Y'Tag if and only if Y in TX'Class. Then we could easily walk back in the derivation tree until the tags match. **************************************************************** From: Randy Brukardt Sent: Friday, July 13, 2001 5:33 PM > Uh-hu, where does this lead us? Personally, I think it is into an impenetrable morass. Remember that tagged types can add discriminants when they are derived, and moreover cannot have initialized discriminants. Thus, you have to get the discriminants on an object declaration, but with this attribute, there would be no way to know what they are! Also, as Christoph noted, if the tag represented an abstract type, you couldn't declare an object. I think these would have to be restricted to very specific contexts. One way would be to declare them indefinite (which would prevent object declarations, and many sorts of generic matching), but I'm not sure that's enough. 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. > And it seems to me that for the problem of the common ancestor, we would need > more for a nice solution: Tags could be partially ordered: > > function "<=" (Left, Right: Tag) return Boolean; > > Returns True if Right is in the derivation tree of Left, else False. > > X: TX; > Y: TY; > > X'Tag <= Y'Tag if and only if Y in TX'Class. > > Then we could easily walk back in the derivation tree until the tags match. Right. We still need a way to get the parent tag or something like that. The above would work, as would my original 'Parent_Tag. (I really think it is a horrible idea to introduce types that aren't known at compile time; I suspect that is why the 'Parent_Type was rejected.) 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??) ****************************************************************