comp.lang.ada
 help / color / mirror / Atom feed
From: "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de>
Subject: Re: OO problem: Performing actions on messages (very long, sorry)
Date: Tue, 4 Jan 2005 18:47:50 +0100
Date: 2005-01-04T18:47:50+01:00	[thread overview]
Message-ID: <cbqe9mwbnb6g.xlhb5hpreghg.dlg@40tude.net> (raw)
In-Reply-To: 1104852099.054703.265080@f14g2000cwb.googlegroups.com

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



  reply	other threads:[~2005-01-04 17:47 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-12-22 13:49 OO problem: Performing actions on messages (very long, sorry) per
2004-12-22 15:17 ` Dmitry A. Kazakov
2004-12-22 16:28 ` Martin Krischik
2004-12-22 17:42   ` per
2004-12-22 18:16     ` Martin Krischik
2004-12-22 19:54     ` Dmitry A. Kazakov
2005-01-03 12:37       ` per
2005-01-03 14:14         ` Dmitry A. Kazakov
2005-01-04 12:05           ` per
2005-01-04 13:30             ` Dmitry A. Kazakov
2005-01-04 15:21               ` per
2005-01-04 17:47                 ` Dmitry A. Kazakov [this message]
2005-01-05 12:01                   ` per
2005-01-05 13:23                     ` Dmitry A. Kazakov
2005-01-05 15:59                       ` per
2005-01-05 20:44                         ` Dmitry A. Kazakov
2005-01-10  8:42                           ` per
2005-01-10 14:22                             ` Dmitry A. Kazakov
2005-01-10 16:24                               ` per
2005-01-10 19:09                                 ` Dmitry A. Kazakov
2005-01-11  9:06                                   ` per
2004-12-22 17:46   ` per
2004-12-22 18:02     ` Martin Krischik
2005-01-03 10:05       ` per
2004-12-22 18:35     ` u_int32_t
2004-12-22 18:38       ` u_int32_t
2004-12-24 18:52 ` Nick Roberts
2005-01-03 16:59   ` per
2005-01-10 12:10   ` per
2005-01-10 13:49     ` Marius Amado Alves
2005-01-10 21:54 ` Simon Wright
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox