* When to use 'Class in a parameter list @ 2001-07-23 20:27 Marin David Condic 2001-07-23 21:39 ` Ehud Lamm ` (3 more replies) 0 siblings, 4 replies; 14+ messages in thread From: Marin David Condic @ 2001-07-23 20:27 UTC (permalink / raw) O.K. Here's something I thought I understood but given behavior of some code I have, now I'm questioning what is happening. If I have a tagged type "Base_Type" that has an operation on it, called "Op" and I derive a new type called "Child_Type" that does not require changes to "Op", do I make the parameter Base_Type or Base_Type'Class? (My understanding was that I could make it Base_Type, but then calls to it with a Child_Type would require explicit type conversion. Making it 'Class would accept anything of that class without conversion. Apparently the compiler is swollowing it without type conversion - which is now confusing me.) In code: procedure Op (Base : in out Base_Type) ; ... X : Child_Type ; ... Op (X) ; -- Why is this working without a Base_Type (X) conversion??? So unless I'm doing something strange that is causing some corner-case to come up, I'm now wondering why I would need Base_Type'Class as a parameter type? I was under the impression that I would use 'Class if I wanted to make an operation that worked on anything derived from the class without explicit conversion. (Possible to override it in a child class, AFAIK...) My understanding of when to *NOT* use the 'Class was if I was building an operation I expected to override (possibly calling the parent operation within it - using a type conversion). So when it is not overriden, and control goes to the parent op without an explicit conversion, then when do you need the 'Class? I must be missing something here......(I need to do this sort of thing more often - it all evaporates if you don't use it!!!) MDC -- Marin David Condic Senior Software Engineer Pace Micro Technology Americas www.pacemicro.com Enabling the digital revolution e-Mail: marin.condic@pacemicro.com Web: http://www.mcondic.com/ ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic @ 2001-07-23 21:39 ` Ehud Lamm 2001-07-24 12:49 ` Marin David Condic 2001-07-23 22:55 ` Stephen Leake ` (2 subsequent siblings) 3 siblings, 1 reply; 14+ messages in thread From: Ehud Lamm @ 2001-07-23 21:39 UTC (permalink / raw) Marin David Condic <marin.condic.auntie.spam@pacemicro.com> wrote in message news:9ji1b3$4pi$1@nh.pace.co.uk... > So unless I'm doing something strange that is causing some corner-case to > come up, I'm now wondering why I would need Base_Type'Class as a parameter > type? I was under the impression that I would use 'Class if I wanted to make > an operation that worked on anything derived from the class without explicit > conversion. (Possible to override it in a child class, AFAIK...) You mean _impossible_ right? Basically if you want overriding to be an option you use a primitive operation. Notice that you have to think about the possible evolution of the system, not about how things work the first time around. The main question is not about the conversion, but whether calling the routine requires run-time dispatching. (essentialy, the class-wide routine doesn't need dispatching). The Rationale has a couple of tables that nicely show the dispatching/non-dispatching semantics of the various parameter lists. It may be helpful to think of class-wide routines as a language incarnation of the "template method" design pattern. They allow you to specify a general alogirthm that may work differently on different derived types, by invoking dispatching routines. (Now, I must admit, this would work better with MI, but MI is evil...) Ehud Lamm ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-23 21:39 ` Ehud Lamm @ 2001-07-24 12:49 ` Marin David Condic 2001-07-24 14:39 ` Dmitry A. Kazakov 2001-07-24 15:16 ` Ehud Lamm 0 siblings, 2 replies; 14+ messages in thread From: Marin David Condic @ 2001-07-24 12:49 UTC (permalink / raw) "Ehud Lamm" <mslamm@mscc.huji.ac.il> wrote in message news:9ji5j7$hlu$1@news.huji.ac.il... > > type? I was under the impression that I would use 'Class if I wanted to > make > > an operation that worked on anything derived from the class without > explicit > > conversion. (Possible to override it in a child class, AFAIK...) > > > You mean _impossible_ right? > Uhhhh Dunno... I seem to have a procedure at one level that takes an Object'Class parameter and I overrode it one level up with an identical procedure taking an Object_Child'Class. The compiler didn't complain and the code appears to work. Maybe it shouldn't, but there you have it... > Basically if you want overriding to be an option you use a primitive > operation. Notice that you have to think about the possible evolution of the > system, not about how things work the first time around. > Primitive operation? What do you mean? Like if I have a "procedure C (Obj : in out Object);" as opposed to "procedure C (Obj : in out Object'Class);"? > The main question is not about the conversion, but whether calling the > routine requires run-time dispatching. (essentialy, the class-wide routine > doesn't need dispatching). The Rationale has a couple of tables that nicely > show the dispatching/non-dispatching semantics of the various parameter > lists. > O.K. So if I make it a 'Class, it does not require runtime dispatching? The compiler knows from the type that it can simply degenerate to the specific operation, rather than jump to a dispatch table? And if I override the 'Class operation (it *seems* to work!) with a new 'Class operation, the compiler can tell from the data type what to jump to? > It may be helpful to think of class-wide routines as a language incarnation > of the "template method" design pattern. They allow you to specify a general > alogirthm that may work differently on different derived types, by invoking > dispatching routines. > (Now, I must admit, this would work better with MI, but MI is evil...) > Now you've got me thinking it should dispatch. Does it or does it not? I sort of conceptualized the 'Class operations (based on examples from a couple of books - which I badly need to re-read apparently! :-) as "This procedure should work on anything derived from this class because it only operates on the base-level components and generally should not be something that is overriden". Whereas, the operations on some base type without the 'Class operation seemed to be saying to me "O.K. This operation works on the base type, but you will probably be overriding it to provide additional capabilities when you derive a child type." Perhaps my conception of this is inaccurate... Maybe you can help me get my brain straightened out about this. I vaguely remember understanding this a couple of years ago when I was last fooling with it, but not having used it recently or enough, it is confusing me again. MDC -- Marin David Condic Senior Software Engineer Pace Micro Technology Americas www.pacemicro.com Enabling the digital revolution e-Mail: marin.condic@pacemicro.com Web: http://www.mcondic.com/ ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-24 12:49 ` Marin David Condic @ 2001-07-24 14:39 ` Dmitry A. Kazakov 2001-07-24 15:16 ` Ehud Lamm 1 sibling, 0 replies; 14+ messages in thread From: Dmitry A. Kazakov @ 2001-07-24 14:39 UTC (permalink / raw) On Tue, 24 Jul 2001 08:49:36 -0400, "Marin David Condic" <marin.condic.auntie.spam@pacemicro.com> wrote: >O.K. So if I make it a 'Class, it does not require runtime dispatching? The >compiler knows from the type that it can simply degenerate to the specific >operation, rather than jump to a dispatch table? No there is no specific operation. A class-wide operation works for all derived types. >Now you've got me thinking it should dispatch. Does it or does it not? Never. A class-wide actual is passed as-is. >I sort of conceptualized the 'Class operations (based on examples from a >couple of books - which I badly need to re-read apparently! :-) as "This >procedure should work on anything derived from this class because it only >operates on the base-level components and generally should not be something >that is overriden". Whereas, the operations on some base type without the >'Class operation seemed to be saying to me "O.K. This operation works on the >base type, but you will probably be overriding it to provide additional >capabilities when you derive a child type." Perhaps my conception of this is >inaccurate... > >Maybe you can help me get my brain straightened out about this. I vaguely >remember understanding this a couple of years ago when I was last fooling >with it, but not having used it recently or enough, it is confusing me >again. I consider class-wide operations as a substitution for generic routines parametrized by a type. The possible values of the generic parameter are restricted to the class (the set of derived types). The goal is same as for generics: to write code that works for some set of types. Advantages are: no instantiation required, a class-wide operation can be put into a library etc. Regards, Dmitry Kazakov ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-24 12:49 ` Marin David Condic 2001-07-24 14:39 ` Dmitry A. Kazakov @ 2001-07-24 15:16 ` Ehud Lamm 2001-07-24 17:16 ` Marin David Condic 1 sibling, 1 reply; 14+ messages in thread From: Ehud Lamm @ 2001-07-24 15:16 UTC (permalink / raw) Marin David Condic <marin.condic.auntie.spam@pacemicro.com> wrote in message news:9jjqt0$o1s$1@nh.pace.co.uk... > "Ehud Lamm" <mslamm@mscc.huji.ac.il> wrote in message > news:9ji5j7$hlu$1@news.huji.ac.il... > > > type? I was under the impression that I would use 'Class if I wanted to > > make > > > an operation that worked on anything derived from the class without > > explicit > > > conversion. (Possible to override it in a child class, AFAIK...) > > > > > > You mean _impossible_ right? > > > Uhhhh Dunno... I seem to have a procedure at one level that takes an > Object'Class parameter and I overrode it one level up with an identical > procedure taking an Object_Child'Class. The compiler didn't complain and the > code appears to work. Maybe it shouldn't, but there you have it... > I didn't mean to imply that you can't write this kind of routine. Only that the intuition is the reverse. But this is *not* overriding exactly, since the inheritance relation is between Object and Object_Child and not between the class-wide types. Again, the real isssue is dispatching, as explained in the retional. You may want to keep in mind that we are not just thinking about formal parameters. The language has class-wide types, so you can have class-wide value (usually an access Obj'class kind of thing). The important relations are between these and dispatching, as shown in this table from the Rationale: +----------+---------------------------------+ | | formal | | actual | specific | class-wide | +----------+----------------+----------------+ | | | | | specific | static binding| class-wide op | | | | | |----------+-------------- --+----------------| | | | | |class-wide| dispatching | class-wide op | | | | | +----------+---------------------------------+ Table 4-1: Kinds of Binding An operand used to control dispatching is called a controlling operand. A primitive operation may have several controlling operands; a primitive function may also have a controlling result. (Sorry if the mail software destroys the layout...) > > > Basically if you want overriding to be an option you use a primitive > > operation. Notice that you have to think about the possible evolution of > the > > system, not about how things work the first time around. > > > Primitive operation? What do you mean? Like if I have a "procedure C (Obj : > in out Object);" as opposed to "procedure C (Obj : in out Object'Class);"? Yes. Again let me quote the paragraph from the Rationale: "With the increased importance of derived types for object oriented programming in Ada 95, the notion of the operations closely related to a type in this manner is generalized. The primitive operations of a type are those that are implicitly provided for the type and, for types immediately declared in a package specification, all subprograms with an operand or result of the type declared anywhere in that package specification. The domain is therefore extended to include the private part (but not the body). Thus, in Ada 95, the derivable operations of Ada 83 have become "primitive operations" and the restriction of these operations to the visible part of a package has been eliminated. These changes support added capability: primitive operations may be private and a type and its derivatives may be declared in the same declarative region (this property is useful for building related abstractions and was used in the package New_Alert_System of Part One). Primitive operations clarify the notion of an abstract data type for purposes of object oriented programming (inheritance and polymorphism) and genericity. They are distinguished from the other operations of a type in the following ways * Inheritance. Primitive operations are the derivable (inherited) operations. * Polymorphism. Primitive operations are dispatching operations on tagged types. * Genericity. Primitive operations are the ones available within generic templates parameterized by a class. " The key line here is the one about polymorphism. > > > > The main question is not about the conversion, but whether calling the > > routine requires run-time dispatching. (essentialy, the class-wide routine > > doesn't need dispatching). The Rationale has a couple of tables that > nicely > > show the dispatching/non-dispatching semantics of the various parameter > > lists. > Now you've got me thinking it should dispatch. Does it or does it not? I > sort of conceptualized the 'Class operations (based on examples from a > couple of books - which I badly need to re-read apparently! :-) as "This > procedure should work on anything derived from this class because it only > operates on the base-level components and generally should not be something > that is overriden". Whereas, the operations on some base type without the > 'Class operation seemed to be saying to me "O.K. This operation works on the > base type, but you will probably be overriding it to provide additional > capabilities when you derive a child type." Perhaps my conception of this is > inaccurate... > The way I explain it to my students is like this "from the 'outside' of the class-wide routine there no dispatching" (i.e., no dispatching on call to the class-wide routine), "from the 'inside' you can have dispathcing, by calling the dispatching operations" (i.e, calls to primitive routine, made inside the class-wide routine). Notice that when you call a routine that expect a specific type, from inside a class-wide routine you are, in a sense, giving a class-wide actual - and per the table above you may have dispatching. Another way at looking at this, is to realize that the class-wide operation behave the same for all type in the hierarchy, up to calls to dispatching routines. The commonality is expressed by creating a class-wide routine; the routine can be sensitive to the differences between the types in the hierarchy by calling type specific (dispatching) routines. Aside from the concept of "template function pattern", another common term is "factoring out common behaviours" - this may give a better idea what's this all about from a design POV. Ehud Lamm ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-24 15:16 ` Ehud Lamm @ 2001-07-24 17:16 ` Marin David Condic 0 siblings, 0 replies; 14+ messages in thread From: Marin David Condic @ 2001-07-24 17:16 UTC (permalink / raw) ...And then it hit me like the wet kiss at the end of a fist.... (Lightbulb!) Ahhhhhh! Its all coming back to me as if out of an alcoholic stupor... O.K. This has me pretty much back on track. I want the class-wide flavor if inside the routine I want to generalize some part of the operation and possibly dispatch to other operations that may depend on the specific type of object sent in. I really need to work with this stuff more than once a year... :-) MDC -- Marin David Condic Senior Software Engineer Pace Micro Technology Americas www.pacemicro.com Enabling the digital revolution e-Mail: marin.condic@pacemicro.com Web: http://www.mcondic.com/ "Ehud Lamm" <mslamm@mscc.huji.ac.il> wrote in message news:9jk3lr$ki0$1@news.huji.ac.il... > > > The way I explain it to my students is like this "from the 'outside' of the > class-wide routine there no dispatching" (i.e., no dispatching on call to > the class-wide routine), "from the 'inside' you can have dispathcing, by > calling the dispatching operations" (i.e, calls to primitive routine, made > inside the class-wide routine). > Notice that when you call a routine that expect a specific type, from > inside a class-wide routine you are, in a sense, giving a class-wide > actual - and per the table above you may have dispatching. > > Another way at looking at this, is to realize that the class-wide operation > behave the same for all type in the hierarchy, up to calls to dispatching > routines. The commonality is expressed by creating a class-wide routine; the > routine can be sensitive to the differences between the types in the > hierarchy by calling type specific (dispatching) routines. > Aside from the concept of "template function pattern", another common term > is "factoring out common behaviours" - this may give a better idea what's > this all about from a design POV. > > Ehud Lamm > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic 2001-07-23 21:39 ` Ehud Lamm @ 2001-07-23 22:55 ` Stephen Leake 2001-07-25 19:20 ` Deligation with Ada95 Hans-Olof Danielsson 2001-07-24 2:22 ` When to use 'Class in a parameter list Vincent Marciante 2001-10-29 22:52 ` Matthew Heaney 3 siblings, 1 reply; 14+ messages in thread From: Stephen Leake @ 2001-07-23 22:55 UTC (permalink / raw) "Marin David Condic" <marin.condic.auntie.spam@pacemicro.com> writes: > O.K. Here's something I thought I understood but given behavior of some code > I have, now I'm questioning what is happening. > > <snip> > > In code: > > procedure Op (Base : in out Base_Type) ; > ... just for clarity: type Child_Type is new Base_Type; > X : Child_Type ; > ... > Op (X) ; -- Why is this working without a Base_Type (X) > conversion??? Because when you declare a derived type (Child_Type), you implicitly declare all the 'primitive operations' of the parent type, for the child type. So you now have: procedure Op (Base : in out Child_Type); This is the whole idea of derived types; it is important to understand this feature. > So unless I'm doing something strange that is causing some corner-case to > come up, I'm now wondering why I would need Base_Type'Class as a parameter > type? I was under the impression that I would use 'Class if I wanted to make > an operation that worked on anything derived from the class without explicit > conversion. Yes, this is true. > (Possible to override it in a child class, AFAIK...) I'm not clear what you mean by this. If you declare: procedure Class_Wide_Op (Foo : in Base_Type'class); then it is _not_ a 'primitive operation', since the type of the argument is _not_ 'Base_Type' (Base_type'class is a _different_ type!). Only 'primitive operations' can be overridden in derived types. > My understanding of when to *NOT* use the 'Class was if I was building an > operation I expected to override (possibly calling the parent operation > within it - using a type conversion). So when it is not overriden, and > control goes to the parent op without an explicit conversion, then when do > you need the 'Class? I must be missing something here......(I need to do > this sort of thing more often - it all evaporates if you don't use > it!!!) Hmm. When you do not explicitly override Op, the implicitly declared operation gets an implicit body that is the same as the parent body. Another difference between a 'Class parameter and a plain parameter; when you call a primitive operation with a 'Class value, you get run-time dynamic dispatching. When you call a primitive operation with a plain value, you get compile-time static dispatching. I'm not clear if this matters in your case. -- -- Stephe ^ permalink raw reply [flat|nested] 14+ messages in thread
* Deligation with Ada95 2001-07-23 22:55 ` Stephen Leake @ 2001-07-25 19:20 ` Hans-Olof Danielsson 2001-07-26 2:06 ` Lao Xiao Hai 0 siblings, 1 reply; 14+ messages in thread From: Hans-Olof Danielsson @ 2001-07-25 19:20 UTC (permalink / raw) To: comp.lang.ada Hi, Some experts state that _deligation_ is a technique used in OOP to avoid subclassing reducing the complexity of a system. If some of you know recourses, other than ACM Ada Letters, Nov/Dec 1994, covering this technique applied with Ada 95, would you please apoint me those other recources. Tia HOD ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Deligation with Ada95 2001-07-25 19:20 ` Deligation with Ada95 Hans-Olof Danielsson @ 2001-07-26 2:06 ` Lao Xiao Hai 0 siblings, 0 replies; 14+ messages in thread From: Lao Xiao Hai @ 2001-07-26 2:06 UTC (permalink / raw) Hans-Olof Danielsson wrote: > Hi, > > Some experts state that _deligation_ is a technique used in OOP > to avoid subclassing reducing the complexity of a system. If some > of you know recourses, other than ACM Ada Letters, Nov/Dec > 1994, covering this technique applied with Ada 95, > would you please apoint me those other recources. The best example of delegation in Ada 95 is the requeue. In delegation, one object asks another to do the actual work requested of it. That class can then forget about the delegation. The delegated class can respond directly to the original requestor. This is not like a procedure call where the original request must unwind through a stack. It is not like an entry call where the request is kept on the queue of the accept statement. The actual requeue does not block the delegator the way an entry call would. There are actually very few languages that implement this idea directly. C++, in its first design, was intended to include this feature, but it was eliminated for some reason. Richard Riehle AdaWorks Software Engineering richard@adaworks.com ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic 2001-07-23 21:39 ` Ehud Lamm 2001-07-23 22:55 ` Stephen Leake @ 2001-07-24 2:22 ` Vincent Marciante 2001-07-24 12:52 ` Marin David Condic 2001-10-29 22:52 ` Matthew Heaney 3 siblings, 1 reply; 14+ messages in thread From: Vincent Marciante @ 2001-07-24 2:22 UTC (permalink / raw) Marin David Condic wrote: > > In code: > > procedure Op (Base : in out Base_Type) ; > ... > X : Child_Type ; > ... > Op (X) ; -- Why is this working without a Base_Type (X) conversion??? > Assuming that Op is defined as a primative of Base_Type then a procedure procedure Op (Base : in out Child_Type) ; automaticaly exists as a consequence of having derived Child_Type from Base_Type. This is the case for all derived types and has nothing to do with the taggedness of the types in question. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-24 2:22 ` When to use 'Class in a parameter list Vincent Marciante @ 2001-07-24 12:52 ` Marin David Condic 2001-07-24 14:36 ` Ed Falis 0 siblings, 1 reply; 14+ messages in thread From: Marin David Condic @ 2001-07-24 12:52 UTC (permalink / raw) O.K. I think I've got my brain straight about that - maybe the type conversion that I thought I needed only needed to occur within the child package in order to force a call back to its parent routine. (Resolving an otherwise ambiguous call.) But that still leaves me a bit confused about when I want to make a parameter of 'Class. MDC -- Marin David Condic Senior Software Engineer Pace Micro Technology Americas www.pacemicro.com Enabling the digital revolution e-Mail: marin.condic@pacemicro.com Web: http://www.mcondic.com/ "Vincent Marciante" <marciant_antispam@li.net> wrote in message news:3B5CDBCA.5810@li.net... > > Assuming that Op is defined as a primative of Base_Type then > a procedure > > procedure Op (Base : in out Child_Type) ; > > automaticaly exists as a consequence of having derived > Child_Type from Base_Type. > > This is the case for all derived types and has nothing > to do with the taggedness of the types in question. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-24 12:52 ` Marin David Condic @ 2001-07-24 14:36 ` Ed Falis 2001-07-24 15:29 ` Ehud Lamm 0 siblings, 1 reply; 14+ messages in thread From: Ed Falis @ 2001-07-24 14:36 UTC (permalink / raw) Marin David Condic wrote: > > O.K. I think I've got my brain straight about that - maybe the type > conversion that I thought I needed only needed to occur within the child > package in order to force a call back to its parent routine. (Resolving an > otherwise ambiguous call.) > > But that still leaves me a bit confused about when I want to make a > parameter of 'Class. There are two situations I find useful for using classwide parameters. The first is when I want to force that the implementation of a routine will not be overridden by derived type. The second is for implementing a "template pattern", where some overall processing is applied regardless of the specific type of the parameter, and dispatching is used within the routine to handle variation among specific types. For instance, when traversing a tree, certain steps are applied to all nodes, whether internal or leaf, while others depend on the status of the node (composite pattern). So here the traversal operation would use a classwide parameter, while the action applied within it, would dispatch. - Ed ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-24 14:36 ` Ed Falis @ 2001-07-24 15:29 ` Ehud Lamm 0 siblings, 0 replies; 14+ messages in thread From: Ehud Lamm @ 2001-07-24 15:29 UTC (permalink / raw) Ed Falis <efalis@mediaone.net> wrote in message news:3B5D8819.E9D8AD4E@mediaone.net... > There are two situations I find useful for using classwide parameters. > > The first is when I want to force that the implementation of a routine > will not be overridden by derived type. Indedd. I failed to mention this reason, since it seemed like this is what Marin focused on, where as I find the second reason of greater design importance. But it is useful to keep this in mind (as well as trick for making routines non-primitive, like declaring them in a child unit). > > The second is for implementing a "template pattern", where some overall > processing is applied regardless of the specific type of the parameter, > and dispatching is used within the routine to handle variation among > specific types. For instance, when traversing a tree, certain steps are > applied to all nodes, whether internal or leaf, while others depend on > the status of the node (composite pattern). So here the traversal > operation would use a classwide parameter, while the action applied > within it, would dispatch. > Another example is having a "traversable" interface with get_first/get_next/end_of_sequence operations, and creating various general algorithms (like filters). Ehud Lamm ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: When to use 'Class in a parameter list 2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic ` (2 preceding siblings ...) 2001-07-24 2:22 ` When to use 'Class in a parameter list Vincent Marciante @ 2001-10-29 22:52 ` Matthew Heaney 3 siblings, 0 replies; 14+ messages in thread From: Matthew Heaney @ 2001-10-29 22:52 UTC (permalink / raw) "Marin David Condic" <marin.condic.auntie.spam@pacemicro.com> wrote in message news:9ji1b3$4pi$1@nh.pace.co.uk... > O.K. Here's something I thought I understood but given behavior of some code > I have, now I'm questioning what is happening. > > If I have a tagged type "Base_Type" that has an operation on it, called "Op" > and I derive a new type called "Child_Type" that does not require changes to > "Op", do I make the parameter Base_Type or Base_Type'Class? Let's go back to Ada83, which already had inheritance (of "primitive" operations): package P is type T is limited private; procedure Op (O : in out T); ... end P; Now let's create a type that derives from T: with P; package Q is type NT is new P.T; end Q; The type NT inherits the operation Op from its parent type T, which means I can do this: declare O : Q.NT; begin Q.Op (O); end; The point is that at the point of declaration of NT, operation Op is "implicitly declared." This was true in Ada83, and it continues to be true in Ada95. (This is quite deliberate: Tucker's philosophy was that Ada95 build on the existing infrastructure already present in Ada83.) Now let's review what we mean by "primitive" operation. Basically, it's an operation that takes the type as a parameter or return value, and is declared in the same package as the type. The operation P.Op is primitive for type P.T, because it takes type T as a parameter. Not all operations that take the type as a parameter are primitive. For example: with P; package PP is procedure Another_Op (O : in out P.T); end; Here, operation Another_Op is NOT primitive. We care about "primitive" operations for a type, because they are inherited during a derivation. That's why type Q.NT automatically has an operation Q.Op -- because it inherited it from its parent P.T. For whatever reason, this is an aspect of Ada that few programmers seem to fully understand. Of course, type NT is free to override Op, if it doesn't like the default implementation: with P; package Q is type NT is new P.T; procedure Op (O : in out NT); end Q; Everything I've just said applies to Ada95. The only difference between Ada83 and Ada95 is that Ada95 added "type extension" to the language. The inheritance model *already* existed in Ada83. The code above is an Ada95 program, but for the example let's rewrite it to use tagged types: package P is type T is tagged limited private; procedure Op (O : in out T); ... end P; with P; package Q is type NT is new P.T with null record; end; (Note that in general, you should create a package hierarchy that mimics the type hierarchy. Here we're trying to keep the example simple.) Everything is the same as in our earlier example. Type Q.NT inherits the operation Op from its parent type P.T, so you can do this (same as before): declare O : Q.NT; begin Q.Op (O); end; Now, we talked about "primitive" operations. Operation Op is primitive for type T (and NT), because it takes type T as a parameter, and is declared in the same package as T (here, package P). You asked whether you should declare the parameter as type T'Class: the answer is NO. The reason is that were T'Class the type, then the operation doesn't satisfy the criteria for "primitiveness". The pararmeter type would be T'Class, not T, and the operation has to take type T in order for it to qualify as "primitive". Operations that are NOT primitive for the type are NOT inherited during a derivation. So if you were to do this: package P is type T is tagged limited private; procedure Op (O : in out T'Class); ... end P; with P; package Q is type NT is new P.T with null record; end; declare O : Q.NT; begin Q.Op (O); --will not compile end; This will NOT compile, because type Q.NT does NOT have an operation called Op. Op is not primitive, and therefore it is not inherited. You want to know when you should declare the operation as taking type T'Class, but your question should really be phrased as "when should the operation be primitive?", or "when should an operation be class-wide?". Does the operation apply to every type in the class, or does it make sense for derived types to provide their own type-specific implementation? One characteristic of class-wide operations is that they have a fixed algorithm, but operations called to implement the algorithm can vary across types. (As Ehud pointed out, this is a design pattern called "Template Method.") Class-wide operations are ultimately implemented by calling primitive operations of the type, which dispatch according to the tag of the object. > (My > understanding was that I could make it Base_Type, but then calls to it with > a Child_Type would require explicit type conversion. You are confused. You don't need to convert Child_Type to Base_Type in order to call Op, because Child_Type already has an operation called Op. The only reason you'd need to do a conversion (here, a "view" conversion) is to call the parent's implementation of Op. For example: package P is type Base_Type is tagged null record; procedure Op (O : in out T); end; package P.C is type Child_Type is new Base_Type with null record; end; declare O : P.C.Child_Type; begin P.C.Op (O); --OK end; This is a perfectly reasonable thing to do. No conversion is required. Now let's say Child_Type overrides Op: package P.C is type Child_Type is new Base_Type with null record; procedure Op (O : in out Child_Type); end; Now let's implement P.C.Op, by calling the parent version of Op: package body P.C is procedure Op (O : in out Child_Type) is begin Op (Base_Type(O)); --do some more stuff end; end P.C; Here we've performed a "view" conversion, in order to call the Op defined for Base_Type. You often do this when implementing a type that derives from Controlled: package P is type T is new Limited_Controlled with null record; procedure Finalize (O : in out T); end; package P.C is type NT is new T with null record; procedure Finalize (O : in out NT); end; package body P.C is procedure Finalize (O : in out NT) is begin --do type-specific clean-up Finalize (T(O)): end; ... end P.C; This technique ensures that "base class" finalization is done too. > Making it 'Class would > accept anything of that class without conversion. Apparently the compiler is > swollowing it without type conversion - which is now confusing me.) No type conversion is necessary. If the operation is primitive for Base_Type, it is inherited by Child_Type during derivation, so therefore Child_Type has the operation. > In code: > > procedure Op (Base : in out Base_Type) ; > ... > X : Child_Type ; > ... > Op (X) ; -- Why is this working without a Base_Type (X) conversion??? Because you're simply calling the Op defined for Child_Type. Op was implicitly declared at the point of declaration of Child_Type, because Op is primtive, and therefore is inherited during a derivation. > So unless I'm doing something strange that is causing some corner-case to > come up, I'm now wondering why I would need Base_Type'Class as a parameter > type? Making the operation take T'Class changes the semantics of the operation. It means "this operation applies to all types in the class." Same as a static method in C++. > I was under the impression that I would use 'Class if I wanted to make > an operation that worked on anything derived from the class without explicit > conversion. (Possible to override it in a child class, AFAIK...) You appear to be confused about when operations implicitly declared for a type. Op is implicitly declared for Child_Type, because it was primitive for Base_Type, and was therefore inherited. An operation that takes Base_Type'Class works "without conversion" because type Base_Type'Class "covers" type Child_Type. In a sense the types Base_Type'Class and Child_Type have a subtype relationship, the way Positive is related to type Integer. > My understanding of when to *NOT* use the 'Class was if I was building an > operation I expected to override (possibly calling the parent operation > within it - using a type conversion). Yes. Like the Finalize example above. > So when it is not overriden, and > control goes to the parent op without an explicit conversion, then when do > you need the 'Class? It was not overridden, but it is still defined for type Child_Type. > I must be missing something here......(I need to do > this sort of thing more often - it all evaporates if you don't use it!!!) Your confusion probably stems from an incomplete understanding of Ada83 semantics. Regards, Matt ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2001-10-29 22:52 UTC | newest] Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2001-07-23 20:27 When to use 'Class in a parameter list Marin David Condic 2001-07-23 21:39 ` Ehud Lamm 2001-07-24 12:49 ` Marin David Condic 2001-07-24 14:39 ` Dmitry A. Kazakov 2001-07-24 15:16 ` Ehud Lamm 2001-07-24 17:16 ` Marin David Condic 2001-07-23 22:55 ` Stephen Leake 2001-07-25 19:20 ` Deligation with Ada95 Hans-Olof Danielsson 2001-07-26 2:06 ` Lao Xiao Hai 2001-07-24 2:22 ` When to use 'Class in a parameter list Vincent Marciante 2001-07-24 12:52 ` Marin David Condic 2001-07-24 14:36 ` Ed Falis 2001-07-24 15:29 ` Ehud Lamm 2001-10-29 22:52 ` Matthew Heaney
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox