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: Wed, 5 Jan 2005 14:23:50 +0100 Organization: cbb software GmbH Message-ID: <1odpvi9429wrb.r854wtzbr1qm$.dlg@40tude.net> 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> <1104926478.797780.7830@c13g2000cwb.googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Trace: individual.net 0nJ2OCdoNpoJ1Aq1Ie8aSQaaDMUdGf49luwSzgtZGZ/RGSISQ= User-Agent: 40tude_Dialog/2.0.12.1 Xref: g2news1.google.com comp.lang.ada:7476 Date: 2005-01-05T14:23:50+01:00 List-Id: On 5 Jan 2005 04:01:18 -0800, per wrote: >>> 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. (:-)) > > OK, I agree true polymorphism is preferable. > > Do you dare to say that ALL generics *could* be implemented with true > polymorphism? > > If so, do you dare to say that ALL generics *should* be implemented > with true polymorphism? (Just curious.) Well, yes, in my view generics should be depreciated. And future evolution of Ada should pursue the goal of removing any need in generics where possible. Though beware, most of Ada people disagree with me! >>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); > > But... What if the descendant to Message has many fields of the same > type (as definitely is the case for me): > > type Message.M1 is new Message with > record > A : Integer; > B : Integer; > end; > > What field would be assigned by > > procedure Put (M : in out Message.M1; Value : Integer); It is a suspicious design. What is the difference between fields A and B? Only names? If so then there should be an integer array instead. So you go as follows: type Index is new Positive; type Integer_List is array (Index range <>) of Integer; procedure Put (M : in out Message; Value : Integer; Position : Index); type Message.M1 (List_Size : Natural) is new Message with record List : Integer_List (1..Size); end record; If there is some semantic difference between A and B, then from design point of view it has to be mapped into types. So you will have: type Port_Number is new Integer range 1..10_000; type Annual_Income is new Integer range 0..100_000_000; procedure Put (M : in out Message; Value : Port_Number); procedure Put (M : in out Message; Value : Annual_Income); Ada encorages and assists design in terms the application domain, rather than focused on implementation details. >>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. > > OK, some of this is a bit beyond my Ada knowledge I'm afraid (remember > I'm still an Ada newbie ;), but I kind of get your point. Lots of new > stuff for me so next questions may be stupid: > > Would this approach cope with that the actions are not connected to a > certain message *instance* until the point the action should execute? > The action at most only "knows" what *type* (or should I say class) of > message it should execute upon, and the message instance existsonly > close (in time) to the moment the action executes and not under the > actions entire life-time. Handles decouple object scopes. It has advantages and disadvantages. The rule of thumb is that if you can do object scopes statically determinable, do it. The decision depends on your requirements. If action objects are tightly bound to defininte message objects then I would use mix-in instead handles. > When you say "For example Override would just assign one handle to > another without any copying" it seems that you mean a handle to an > entire message? If so, that's not what an override action is supposed > to. Only a single field *inside* the message should be overridden. Or > have I misunderstood you completely? OK, I see. But then the question to answer what purpose serves message apart from being just a container for unrelated data? The difficulties of the design come from that missing pupose. If there is nothing in common then nothing can be programmed for that nothing. (:-)) > I would like to avoid lots of copying since the system is a real-time > simulator so using handles is tempting. In practice I think most > parameters will be "small" though (Integer, Float, bitfields etc) so > it's not a big deal. Hmm, what about this mix-in: package Interfaces is type Message is abstract tagged ... type Action is abstract tagged limited ...; procedure Execute (This : in out Action) is abstract; end Interfaces; package M1_Thinds is type M1 is new Message with record -- Message M1 A : Integer; B : Float; end record; type M1_Action (Data : access M1) is -- Abstract actions on M1 abstract new Action with null record; type Override_A is new M1_Action with record A : Integer; end record; procedure Execute (This : in out Override_A); type Override_B is new M1_Action with record B : Float; end record; procedure Execute (This : in out Override_B); ... package body M1_Things procedure Execute (This : in out Override_A) is begin This.Data.A := This.A; end Execute; procedure Execute (This : in out Override_B) is begin This.Data.B := This.B; end Execute; Here all actions on some type of messages have a "pointer" to the corresponding message object. The scope of an action should be nested in the scope of the message. Here in fact action is a pointer to message extended with some additional data. When scopes get different, then action could be a handle to message. Messages will be collected using reference counting. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de