From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,a8d137db7a5f6c81 X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news1.google.com!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: OO problem: Performing actions on messages (very long, sorry) Date: Tue, 4 Jan 2005 18:47:50 +0100 Organization: cbb software GmbH Message-ID: References: <1103723394.299024.314670@c13g2000cwb.googlegroups.com> <13465377.hrn0RlrJV7@linux1.krischik.com> <1103737351.196460.85450@f14g2000cwb.googlegroups.com> <1qdvdjid4u58v.1xz6j5ec6nfcy$.dlg@40tude.net> <1104755823.837077.74630@z14g2000cwz.googlegroups.com> <8oknh024yb43$.71qlyp6g8y2x$.dlg@40tude.net> <1104840326.160879.132400@c13g2000cwb.googlegroups.com> <1ogeykubpns90.2jnblmdu1wg2.dlg@40tude.net> <1104852099.054703.265080@f14g2000cwb.googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Trace: individual.net JeuWnP4MNvUjczHfa1H9TApzHfwvBba5bY5n8RcFgUXXapZCg= User-Agent: 40tude_Dialog/2.0.12.1 Xref: g2news1.google.com comp.lang.ada:7431 Date: 2005-01-04T18:47:50+01:00 List-Id: On 4 Jan 2005 07:21:39 -0800, per wrote: > Ah, we understand each other! > Dmitry: > >>OK, try this: > >>generic >> type Argument_Type is private; >> type Message_Type is new Message with private; > > The above will force the Message_Type to be an ancestor of Message, > right? So Message_Type is still generic but have some additional > restrictions...? Yes, it is a generic formal parameter with the contract requiring the actual type to be a descendant of Message. >> with procedure Put (This : in out Message_Type; Value : Argument_Type); >>package Action.Generic_Override is >> type Override is new Action with record >> Argument : Argument_Type; >> end record; >> Execute (This : in out Override; Message : in Message'Class); >>end Action.Generic_Override; > >>package body Action.Generic_Override is >> ... >> Execute (This : in out Override; Message : in Message'Class) is >> begin >> if This not in Message_Type'Class then > > Do you mean "Message" instead of "This"? Yes, sorry. The golden rule is always to use compiler before posting examples! (:-() >> Ada.Exceptions.Raise_Exception >> ( Constraint_Error'Identity, >> "Illegal message type in Execute, found " & >> Ada.Tags.External_Tag (Message'Tag) & >> "expected a descendant of:" >> Ada.Tags.External_Tag (Message_Type'Tag) & >> ); > > (Wow!) > >> end if; >> Put (Message_Type'Class (Message), This.Argument); Remember the rule? The above of course should be Put (Message_Type (Message), This.Argument); sorry again! The reason is that Put is not dispatching, because we pass it explicitly as a generic parameter. It is logical that then it cannot dispatch on Message_Type'Class. The compiler should complain. Ada is a safe language! > The conversion is needed to be compatible with the declaration of Put, > right? > > And since argument Message is within Message'Class AND Message_Type is > a descendant of Message it will work? Plus it will check at run time if Message is in Message_Type'Class, only then it can be converted to Message_Type. This check may fail. Note also that because Message is tagged, this conversion is a view conversion. >> end Execute; >> ... > >>You have to instantiate Action.Generic_Override for each combination of >>parameter / message. For example: > >>package Action_on_B_for_M2 is >> new Action.Override >> ( Argument_Type => Float; >> Message_Type => M2; >> Put => Put_B >> ); > > I see! So you're saying that I need one instantiation for each > *Put-procedure* and message (instead of one for each *type* and > message)? Hm, that's a lot of instances in my system... Right, generics are said to be statically polymorphic, to underline that there is a better, true polymorphism. (:-)) > This may be the solution I looked for, and I think I'll give it a try > (as soon as I get used to all those instances :). > > Thanks a lot Dmitry and others for your efforts on this issue! I've > learned lots of other Ada stuff on the way. > > Finally, Dmitry, would this be your solution if you were free to solve > it anyway you like? I would try to get rid of generics. The main problem why generics appear in your case is that you have parallel type hierarchies. No language, I know of, can gracefully handle that. Remove parallelism and things will become much simpler. For example, 1. If the number of different argument types is limited, you could make all possible variants of Put primitive operations of Message: type Message is abstract tagged ... procedure Put (M : in out Message; Value : Ihteger); procedure Put (M : in out Message; Value : Float); ... The default implementations provided by Message will raise Program_Error (that should never happen.) Even with a potentially unlimited number of different argument types, you could handle complexity by deriving their "uncountable subsets' from the same base: type User_Defined_Argument is abstract tagged null record; procedure Put (M : in out Message; Value : User_Defined_Argument'Class); This would convolute one axis of complexity. 2. Another axis is that action have to contain message of potentially unknown type. You already handled that by declaring a common ancestor for all messages. So you could aggregate Message'Class into Action instead of doing so for a specific Message. The latter requires generics, the former does not. Though you cannot do it directly: (*) type Override (Argument : access Message'Class) is new Action with record Argument : Message'Class; -- This is not Ada!! end record; But you can use mix-in: type Override (Argument : access Message'Class) is new Action ...; Here each instance of Override has a "pointer" (access discriminant) to its message. This approach might become difficult when the scope of action is not nested in one of the message. Also it would require Action to be a limited type. So, alternatively you can make reference counting for messages and have handles (smart pointers) to them in actions. There are ready-to use implementations of smart pointers in Ada. With this approach you can pass a handle to message instead of the message object. For example Override would just assign one handle to another without any copying. In some cases it might become much more efficient than parameter marshaling, which is what I suppose you are trying to do. The price to pay is that you will need to handle aliasing, concurrent changes of a message etc. ---- * If action is not a descendant of message. This is why I tried to bring you to this decision, but you were adamant. (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de