comp.lang.ada
 help / color / mirror / Atom feed
* OO problem: Performing actions on messages (very long, sorry)
@ 2004-12-22 13:49 per
  2004-12-22 15:17 ` Dmitry A. Kazakov
                   ` (3 more replies)
  0 siblings, 4 replies; 31+ messages in thread
From: per @ 2004-12-22 13:49 UTC (permalink / raw)


Hi all,

I'm new to Ada (but not to OO). I'm having trouble designing a system,
or at least a key part of it. (I have a few alternative non-tested
solutions but I don't get why this doesn't work.) And I'd like to do it
"by the book" in nice OO Ada coding style.

The system handles "general" messages and "general" actions that may be
performed on the messages. I'm trying to utilize inheritage, polymorphy
and generics to solve this.

You may now:
1. Continue and read the entire post
2. Skip to Code and Questions
3. Skip to Questions

Description
-----------

There's an abstract parent-class, Action, and non-abstract children,
Action.Override, Action.Error, Action.Record etc.

Each action child shall be able to "do something" with a message in the
virtual (is that the Ada term?) procedure Execute.

The action Action.Override serves as an example in this post. This
action shall override a field in a message.

All actions are created and then enqueued in certain action queues in a
preparation phase and then executed (real-time) in the execution phase.
The messages are both created and transported through the system in the
execution phase.

In order to store the value to override with, and where to put it when
performing the override, the Action.Override stores an override
argument (like a Float for instance) and an access to the proper
Put-procedure (like Put_A(M : in out Message.M1.Instance; A : Float))
for a certain message class instance (the specific message the action
shall operate upon). The Put procedure takes as arguments the message
instance and a value to put. The procedure name defines what message
field to put the value into, such as Put_A for field A.

The type of the field to override and the type of the message are
generic parameters in the Action.Override package/class, so there's one
instantiation of Action.Override for each message type and override
type:

package M1_Float_Override_Action is new Action.Override
(ArgumentType => Float,
MessageType  => Message.M1.Instance);

There's another abstract parent-class, Message, and non-abstract child
classes Message.M1, Message.M2 etc.

Each child message has its own set of, non-virtual, Put procedures that
writes the contents of the message. The message contents is a record
with arbitrary width, depth and types.

There will also be a nice general mechanism that basically gets any
type of message, applies any actions enqueued for it and then puts the
message away. It feels like this should be possible with Ada mechanisms
such as inheritage, polymorphy and generics but, I kind of doubt it
now...

Code
----

--Abstract base class Message:

package Message is
type Instance is abstract tagged private;
type Reference is access all Instance;
type Class_Reference is access all Instance'Class;
...
private
type Instance is abstract tagged
record
...
end record;
end Message;
package body Message is
...
end Message;


--Concrete child class Message.M1:

package Message.M1 is
type Instance is new Message.Instance with private;
type Reference is access all Instance;
type Class_Reference is access all Instance'Class;
...
procedure Put_A(Self  : in out Instance;
New_A : in     Float);
procedure Put_B(...
...
private
type Instance is new Message.Instance with
record
A : Float;
B : Integer;
...
end record;
end Message.M1;
package body Message.M1 is
...
procedure Put_A(Self  : in out Instance;
New_A : in     Float) is
begin
Self.A := New_A;
end;
...
end Message.M1;


--Abstract base class Action:

package Action is
type Instance is abstract tagged private;
type Reference is access all Instance;
type Class_Reference is access all Instance'Class;
...
procedure Execute(Self : in out Instance;
M    : in     Message.Class_Reference) is abstract;
...
private
type Instance is abstract tagged
record
...
end record;
end Action;
package body Action is
...
end Action;


--Concrete generic child class Action.Override:

generic
type ArgumentType is private;
type MessageType is private;
package Action.Override is
type Instance is new Action.Instance with private;
type Reference is access all Instance;
type Class_Reference is access all Instance'Class;
...
type OverrideMethodRefType is access procedure(M : in out
MessageType;
A : in
ArgumentType);
procedure Initialize
(Self      : in out Instance;
Tick      : in     Integer;
Arg       : in     ArgumentType;
MethodRef : in     OverrideMethodRefType);

procedure Execute(Self : in out Instance;
M    : in     Message.Class_Reference);
...
private
type Instance is new Action.Instance with
record
Argument          : ArgumentType;
OverrideMethodRef : OverrideMethodRefType;
end record;
end Action.Override;
package body Action.override is
...
procedure Initialize
(Self      : in out Instance;
Tick      : in     Integer;
Arg       : in     ArgumentType;
MethodRef : in     OverrideMethodRefType) is

begin
Initialize(Action.Instance(Self), Tick);
Self.Argument          := Arg;
Self.OverrideMethodRef := MethodRef;
end;

procedure Exe(Self : in out Instance;
M    : in Message.Class_Reference) is
begin
Ada.Text_IO.Put_Line(PACKAGE_NAME & ".Exe Tick=" &
Integer'Image(Self.Tick));
--     Message.Print(M.all);

--Problem area:--
Self.OverrideMethodRef(M.all, Self.Argument); --Compilation error!
-----------------
end;
...
end Action.Override;

Override actions are initialized like this:

Initialize(An_Action, 200, 3.3, Message.M1.Put_A'access);

Compilation error:
------------------
gcc says:

action-override.adb:100:31: expected private type "MessageType" defined
at action-override.ads:35
action-override.adb:100:31: found type "Instance'Class" defined at
message.ads:44
Questions:
----------
I have a class-wide variable (M.all) and want to pass it to a procedure
(Message.M1.Put_A) taking a specific instance (Message.M1.Instance) of
the class.

1. Why does this not work?

2. Shouldn't a conversion from T11 stored in a T1'Class variable, to a
T11 variable be OK of T11 is a child of T1 (checked during run-time)?

The information needed is of course present in M.all, otherwise
dispatching wouldn't be possible. (I have tested dispatching with the
call Message.Print(M.all) commented out in the example and i works
fine.)

Note that making every Put-procedure virtual is not an option since
they by nature are unique to each message.

3. Are there any problems connected to that I use an access to a
procedure?

4. Is the generic type MessageType causing the problem? The MessageType
is supposed to be a specific sub-class of Message, hence a member of
Message'Class so...

5. Have I designed completely wrong?

6. Do you have any suggestions on how to solve this problem in a good
way?

Your answers will be appreciated.

Merry Christmas etc!

/Per




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  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
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 31+ messages in thread
From: Dmitry A. Kazakov @ 2004-12-22 15:17 UTC (permalink / raw)


On 22 Dec 2004 05:49:54 -0800, per wrote:

> I'm new to Ada (but not to OO). I'm having trouble designing a system,
> or at least a key part of it. (I have a few alternative non-tested
> solutions but I don't get why this doesn't work.) And I'd like to do it
> "by the book" in nice OO Ada coding style.
> 
> The system handles "general" messages and "general" actions that may be
> performed on the messages. I'm trying to utilize inheritage, polymorphy
> and generics to solve this.
> 
> You may now:
> 1. Continue and read the entire post
> 2. Skip to Code and Questions
> 3. Skip to Questions
> 
> Description
> -----------
> 
> There's an abstract parent-class, Action, and non-abstract children,
> Action.Override, Action.Error, Action.Record etc.
> 
> Each action child shall be able to "do something" with a message in the
> virtual (is that the Ada term?) procedure Execute.

No. It is called "primitive operation".

> The action Action.Override serves as an example in this post. This
> action shall override a field in a message.

One cannot override a field. Presently Ada's inheritance model is a type
extension one. I.e. you can add a field, but not replace it with another.

> All actions are created and then enqueued in certain action queues in a
> preparation phase and then executed (real-time) in the execution phase.
> The messages are both created and transported through the system in the
> execution phase.
> 
> In order to store the value to override with, and where to put it when
> performing the override, the Action.Override stores an override
> argument (like a Float for instance) and an access to the proper
> Put-procedure (like Put_A(M : in out Message.M1.Instance; A : Float))
> for a certain message class instance (the specific message the action
> shall operate upon). The Put procedure takes as arguments the message
> instance and a value to put. The procedure name defines what message
> field to put the value into, such as Put_A for field A.

It is too late. Basically Put should be a primitive operation of
Message...Instance. But when you pass a type as a formal generic parameter
it is already frozen, which in other words means that you cannot define a
primitive Put for it. You can do instead:

generic
   type ArgumentType is private; -- Type extension
package Message.Extender is
   type Instance is new Message.Instance with record
      Extension : ArgumentType;
   end record;
   procedure Put (M : Instance; A : ArgumentType);
      -- This is a "method" of Instance now

procedure Put (M : Instance; A : ArgumentType) is
begin
   M.Extension := A;
end Put;

> The type of the field to override and the type of the message are
> generic parameters in the Action.Override package/class, so there's one
> instantiation of Action.Override for each message type and override
> type:
> 
> package M1_Float_Override_Action is new Action.Override
> (ArgumentType => Float,
> MessageType  => Message.M1.Instance);
> 
> There's another abstract parent-class, Message, and non-abstract child
> classes Message.M1, Message.M2 etc.

It is not clear what are you going to achieve by that. It will not be
dynamically polymorphic anyway as long as ArgumentTypes are not derived
from the same base. So any possible gain = 0, IMO.

> Each child message has its own set of, non-virtual, Put procedures that
> writes the contents of the message. The message contents is a record
> with arbitrary width, depth and types.

If Put is not dispatching, what the use of it. In particular, why ":=" does
not fit?

> There will also be a nice general mechanism that basically gets any
> type of message, applies any actions enqueued for it and then puts the
> message away. It feels like this should be possible with Ada mechanisms
> such as inheritage, polymorphy and generics but, I kind of doubt it
> now...

It is definitely possible:

package Messages is
   type Message is 
      abstract new Ada.Finalization.Limited_Controlled with private;
   procedure Execute (This : in out Message) is abstract;

   type Messages_Queue is
      [abstract] new Ada.Finalization.Limited_Controlled with private;
   -- Abstract if there should be different implementations of queues
   procedure Enqueue
      (  Queue : in out Messages_Queue;
         Item : in out Message'Class
      )  [is abstract];
private
   ...
end Messages;

package Messages.My_Messages is
   type My_Message is new Message with private;
   procedure Finalize (This : in out Message); -- How to destruct
   procedure Initialize (This : in out Message); -- How to construct
   procedure Execute (This : in out Message); -- What to be done
   procedure Put_A (This : in out Message; A : Something);
   ...

> Code
[...]
> 
> 1. Why does this not work?

The expected type is MessageType as the compiler tells. MessageType is a
generic formal type. The compiler does not know of any its possible
relationships with Message.Instance. It is just a type:

generic
   type MessageType is private;

It is not even tagged.

> 2. Shouldn't a conversion from T11 stored in a T1'Class variable, to a
> T11 variable be OK of T11 is a child of T1 (checked during run-time)?

Yes. If it is not, then Constraint_Error is propagated.

> The information needed is of course present in M.all, otherwise
> dispatching wouldn't be possible. (I have tested dispatching with the
> call Message.Print(M.all) commented out in the example and i works
> fine.)

The information carrier is called "type tag". Each T'Class object has type
tag in it.

> Note that making every Put-procedure virtual is not an option since
> they by nature are unique to each message.
> 
> 3. Are there any problems connected to that I use an access to a
> procedure?

Basically, that it is not much OO. Primitive operations (virtual methods)
are for that.

> 4. Is the generic type MessageType causing the problem? The MessageType
> is supposed to be a specific sub-class of Message, hence a member of
> Message'Class so...

Yes.

> 5. Have I designed completely wrong?

Mmm, it is too complex to my taste. I have an impression that you dealt too
much with C++ templates. (:-))

> 6. Do you have any suggestions on how to solve this problem in a good
> way?

Message = Action to me. Even if there are some constraints like that
several actions to be executed in a defined order, I would stick to this
model using containers of actions.

> Your answers will be appreciated.
> 
> Merry Christmas etc!

And Happy New Year!

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  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 17:46   ` per
  2004-12-24 18:52 ` Nick Roberts
  2005-01-10 21:54 ` Simon Wright
  3 siblings, 2 replies; 31+ messages in thread
From: Martin Krischik @ 2004-12-22 16:28 UTC (permalink / raw)


per wrote:

> Hi all,

> package Message.M1 is
> type Instance is new Message.Instance with private;
> type Reference is access all Instance;
> type Class_Reference is access all Instance'Class;

consider not to use "all". Read:

http://en.wikibooks.org/wiki/Programming:Ada:Types:access#access_vs._access_all

Also you night not need an access at all. Read:

http://en.wikibooks.org/wiki/Programming:Ada:OO#The_class-wide_type

> Your answers will be appreciated.
> 
> Merry Christmas etc!

With Regards

Martin
-- 
mailto://krischik@users.sourceforge.net
http://www.ada.krischik.com



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  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
  2004-12-22 17:46   ` per
  1 sibling, 2 replies; 31+ messages in thread
From: per @ 2004-12-22 17:42 UTC (permalink / raw)


Hi Dimitri, thanx for your answers.

Sorry for the yucky code/text. All indentation and line breaks was
screwed up:( I'm using google groups (for the first time). Not very
user friendly. Had to compose this answer in a separate file and
manually paste in the citations so this answer is also a bit yucky...

>> Each action child shall be able to "do something" with a message in
the
>> virtual (is that the Ada term?) procedure Execute.
>
>No. It is called "primitive operation".

OK. But is there no distinction between operations that may dispatch
and those those who cannot? Execute, for example, may be a primitive
operation of each child regardless of if the parent has declared it or
not. So "primitive operation" doesn't tell that dispatching will be
possible or not, right?

Maybe "dispatching" is the distinction?

>> The action Action.Override serves as an example in this post. This
>> action shall override a field in a message.
>
>One cannot override a field. Presently Ada's inheritance model is a
type
>extension one. I.e. you can add a field, but not replace it with
another.

No! I was unclear. I'm not trying to override anything in that sense.

The messages are basically records filled with values. It's the
_values_ in the existing message structures the Action.Override is
supposed to override:

type Instance is new Message.Instance with
record
A : Float;
B : Integer;
...
end record;
...

Somewhere some mechanism assigns the initial values:
M1 : Message.M1.Instance;
Put_A(M1, 1.0);
Put_B(M1, 1);
...

Later on, an override action shall be able to override these values by
basically doing:

Put_A(M1, Some_Other_Value_Stored_In_The_Action);

again!

Other messages may record the entire message and send it to some place
else, or just extract some specific data field and pass along etc.

>> All actions are created and then enqueued in certain action queues
in a
>> preparation phase and then executed (real-time) in the execution
phase.
>> The messages are both created and transported through the system in
the
>> execution phase.
>
>> In order to store the value to override with, and where to put it
when
>> performing the override, the Action.Override stores an override
>> argument (like a Float for instance) and an access to the proper
>> Put-procedure (like Put_A(M : in out Message.M1.Instance; A :
Float))
>> for a certain message class instance (the specific message the
action
>> shall operate upon). The Put procedure takes as arguments the
message
>> instance and a value to put. The procedure name defines what message
>> field to put the value into, such as Put_A for field A.
>
>It is too late. Basically Put should be a primitive operation of
>Message...Instance. But when you pass a type as a formal generic
parameter
>it is already frozen, which in other words means that you cannot
define a
>primitive Put for it. You can do instead:
>
>generic
>  type ArgumentType is private; -- Type extension
>package Message.Extender is
>  type Instance is new Message.Instance with record
>     Extension : ArgumentType;
>  end record;
>  procedure Put (M : Instance; A : ArgumentType);
>     -- This is a "method" of Instance now
>
>procedure Put (M : Instance; A : ArgumentType) is
>begin
>  M.Extension := A;
>end Put;

You're saying I should replace every single field in every message
record with an extension as above?

I sort of get what you mean, but not entirely. I can't see how my
"complex" message records would look like. Each message consists of
many variables and sub-records. I don't think this is what I look for.

It feels like you're answering another question here... maybe due to
the above misunderstanding?

>> The type of the field to override and the type of the message are
>> generic parameters in the Action.Override package/class, so there's
one
>> instantiation of Action.Override for each message type and override
>> type:
>
>> package M1_Float_Override_Action is new Action.Override
>> (ArgumentType => Float,
>> MessageType  => Message.M1.Instance);
>
>> There's another abstract parent-class, Message, and non-abstract
child
>> classes Message.M1, Message.M2 etc.
>
>It is not clear what are you going to achieve by that. It will not be
>dynamically polymorphic anyway as long as ArgumentTypes are not
derived
>from the same base. So any possible gain = 0, IMO.

Correct. No polymorphy (at least not with this action-stuff). But all
messages has common data and behaviours that I handle in the abstract
base-class. So there's the gain.

>> Each child message has its own set of, non-virtual, Put procedures
that
>> writes the contents of the message. The message contents is a record
>> with arbitrary width, depth and types.
>
>If Put is not dispatching, what the use of it. In particular, why ":="
does
>not fit?

I want to assign values to fields in the messages from the override
actions. How could this be done with ":=" only? I will not implement an
action for EACH field in EACH message.

I would love to make Put dispatching but how would a procedure look
like that can assign a value to any field in a record?

(Yes, I'm still stuck with that record...)

>> There will also be a nice general mechanism that basically gets any
>> type of message, applies any actions enqueued for it and then puts
the
>> message away. It feels like this should be possible with Ada
mechanisms
>> such as inheritage, polymorphy and generics but, I kind of doubt it
>> now...
>
>It is definitely possible:
>
>package Messages is
>  type Message is
>     abstract new Ada.Finalization.Limited_Controlled with private;
>  procedure Execute (This : in out Message) is abstract;
>
>  type Messages_Queue is
>     [abstract] new Ada.Finalization.Limited_Controlled with private;
>  -- Abstract if there should be different implementations of queues
>  procedure Enqueue
>     (  Queue : in out Messages_Queue;
>        Item : in out Message'Class
>     )  [is abstract];
>private
>  ...
>end Messages;
>
>package Messages.My_Messages is
>  type My_Message is new Message with private;
>  procedure Finalize (This : in out Message); -- How to destruct
>  procedure Initialize (This : in out Message); -- How to construct
>  procedure Execute (This : in out Message); -- What to be done
>  procedure Put_A (This : in out Message; A : Something);
>  ...

Ugh, I don't get it.

How could the message know what to do in Execute? There may be 0 or 5
or 500 actions to be taken on each message. The messages do not exist
when the actions are defined and hence can not be prepared with that
information. Where/how do you store the values to override with for
example...?

Maybe this too is an effect of the initial misunderstanding?

But I was enlighted by some other stuff in your example though :)

>> Code
>[...]
>
>> 1. Why does this not work?
>
>The expected type is MessageType as the compiler tells. MessageType is
a
>generic formal type. The compiler does not know of any its possible
>relationships with Message.Instance. It is just a type:
>
>generic
>  type MessageType is private;
>
>It is not even tagged.

I see.

But the dispatching call "Message.Print(M.all)" works and that
procedure expects as a parameter a concrete instance within
Message.Instance'Class. It too gets this unknown MessageType yet the
compiler doesn't whine.

What's the difference?

>> 2. Shouldn't a conversion from T11 stored in a T1'Class variable, to
a
>> T11 variable be OK of T11 is a child of T1 (checked during
run-time)?
>
>Yes. If it is not, then Constraint_Error is propagated.

Yay, I've got something right! :)

>> The information needed is of course present in M.all, otherwise
>> dispatching wouldn't be possible. (I have tested dispatching with
the
>> call Message.Print(M.all) commented out in the example and i works
>> fine.)
>
>The information carrier is called "type tag". Each T'Class object has
type
>tag in it.

OK.

>> Note that making every Put-procedure virtual is not an option since
>> they by nature are unique to each message.
>
>> 3. Are there any problems connected to that I use an access to a
>> procedure?
>
>Basically, that it is not much OO. Primitive operations (virtual
methods)
>are for that.
>
>> 4. Is the generic type MessageType causing the problem? The
MessageType
>> is supposed to be a specific sub-class of Message, hence a member of
>> Message'Class so...
>
>Yes.
>
>> 5. Have I designed completely wrong?
>
>Mmm, it is too complex to my taste. I have an impression that you
dealt too
>much with C++ templates. (:-))

Hehe, actually I've most been programming standard C... :)

But I have (very varying) experience from OO languages such as
TurboPascal (back in the 80's), C++, Ada95 and Java.

>> 6. Do you have any suggestions on how to solve this problem in a
good
>> way?
>
>Message = Action to me. Even if there are some constraints like that
>several actions to be executed in a defined order, I would stick to
this
>model using containers of actions.

Would you still after my clarification?

Cause I'm not really with you there just yet. You're most welcome to
explain what you mean further.

Thanks for your effort! I'm taking a Christmas break for a couple of
days now...

Regards

Per




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2004-12-22 16:28 ` Martin Krischik
  2004-12-22 17:42   ` per
@ 2004-12-22 17:46   ` per
  2004-12-22 18:02     ` Martin Krischik
  2004-12-22 18:35     ` u_int32_t
  1 sibling, 2 replies; 31+ messages in thread
From: per @ 2004-12-22 17:46 UTC (permalink / raw)


Thanks Martin, I'll read your tips as soon as possible. This kind of
general  "how-to-usually-do-stuff" experience is highly appreciated!

(BTW how do you folks do these fancy citations, signatures and stuff?
Via google I can't find any of that...)

Regards

Per




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  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
  1 sibling, 1 reply; 31+ messages in thread
From: Martin Krischik @ 2004-12-22 18:02 UTC (permalink / raw)


per wrote:

> Thanks Martin, I'll read your tips as soon as possible. This kind of
> general  "how-to-usually-do-stuff" experience is highly appreciated!

> (BTW how do you folks do these fancy citations, signatures and stuff?
> Via google I can't find any of that...)

We access the usenet directly with a usenet reader and not via a web
browser.

With Regards

Martin

-- 
mailto://krischik@users.sourceforge.net
http://www.ada.krischik.com



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2004-12-22 17:42   ` per
@ 2004-12-22 18:16     ` Martin Krischik
  2004-12-22 19:54     ` Dmitry A. Kazakov
  1 sibling, 0 replies; 31+ messages in thread
From: Martin Krischik @ 2004-12-22 18:16 UTC (permalink / raw)


per wrote:

> Hi Dimitri, thanx for your answers.
> 
> Sorry for the yucky code/text. All indentation and line breaks was
> screwed up:( I'm using google groups (for the first time). Not very
> user friendly. Had to compose this answer in a separate file and
> manually paste in the citations so this answer is also a bit yucky...
> 
>>> Each action child shall be able to "do something" with a message in
> the
>>> virtual (is that the Ada term?) procedure Execute.
>>
>>No. It is called "primitive operation".
> 
> OK. But is there no distinction between operations that may dispatch
> and those those who cannot? Execute, for example, may be a primitive
> operation of each child regardless of if the parent has declared it or
> not. So "primitive operation" doesn't tell that dispatching will be
> possible or not, right?

All primitve operations dispach.

>>> The action Action.Override serves as an example in this post. This
>>> action shall override a field in a message.
>>
>>One cannot override a field. Presently Ada's inheritance model is a
> type
>>extension one. I.e. you can add a field, but not replace it with
> another.
> 
> No! I was unclear. I'm not trying to override anything in that sense.
> 
> The messages are basically records filled with values. It's the
> _values_ in the existing message structures the Action.Override is
> supposed to override:
 
> type Instance is new Message.Instance with
> record
> A : Float;
> B : Integer;
> ...
> end record;

Maybe you want to read about a "variant record":

http://en.wikibooks.org/wiki/Programming:Ada:Types:record#Variant_Record

It just might get you a new idea. 

> Somewhere some mechanism assigns the initial values:
> M1 : Message.M1.Instance;
> Put_A(M1, 1.0);
> Put_B(M1, 1);

You could try a qualified expression:

http://en.wikibooks.org/wiki/Programming:Ada:Subtypes#Evaluate_As
 
>>procedure Put (M : Instance; A : ArgumentType) is
>>begin
>>  M.Extension := A;
>>end Put;
> 
> You're saying I should replace every single field in every message
> record with an extension as above?

Well, a variant record initialized by a qualified expression might be the
better option.
 
With Regards

Martin
-- 
mailto://krischik@users.sourceforge.net
http://www.ada.krischik.com



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2004-12-22 17:46   ` per
  2004-12-22 18:02     ` Martin Krischik
@ 2004-12-22 18:35     ` u_int32_t
  2004-12-22 18:38       ` u_int32_t
  1 sibling, 1 reply; 31+ messages in thread
From: u_int32_t @ 2004-12-22 18:35 UTC (permalink / raw)


On Wed, 22 Dec 2004 09:46:52 -0800, per wrote:

> (BTW how do you folks do these fancy citations, signatures and stuff?
> Via google I can't find any of that...)

news.individual.net lets you sign up for a newsgroup account (not
automated). This is very useful for me as I don't have a permanent place
to access the net yet, but I'm getting there :)



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2004-12-22 18:35     ` u_int32_t
@ 2004-12-22 18:38       ` u_int32_t
  0 siblings, 0 replies; 31+ messages in thread
From: u_int32_t @ 2004-12-22 18:38 UTC (permalink / raw)


On Wed, 22 Dec 2004 18:35:19 +0000, u_int32_t wrote:

>> (BTW how do you folks do these fancy citations, signatures and stuff?
>> Via google I can't find any of that...)
> 
> news.individual.net lets you sign up for a newsgroup account (not
> automated). This is very useful for me as I don't have a permanent place
> to access the net yet, but I'm getting there :)

Of course the hidden message is that you no longer need google groups.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  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
  1 sibling, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2004-12-22 19:54 UTC (permalink / raw)


On 22 Dec 2004 09:42:31 -0800, per wrote:

>>> Each action child shall be able to "do something" with a message in the
>>> virtual (is that the Ada term?) procedure Execute.
>>
>>No. It is called "primitive operation".
> 
> OK. But is there no distinction between operations that may dispatch
> and those those who cannot?

For tagged types it will dispatch on a class-wide argument.

> Execute, for example, may be a primitive
> operation of each child regardless of if the parent has declared it or
> not.

No if parent has it, then child overrides it. So a primitive operation of a
tagged type is polymorphic, i.e. defined on the whole class of types
derived from the parent type that has declared it. Override is a part of
the implementation of that operation selected when the actual parameter is
of the derived type.

> So "primitive operation" doesn't tell that dispatching will be
> possible or not, right?

For tagged types it will.

> Maybe "dispatching" is the distinction?

Dispatch = selection of an implementation.

>>> The action Action.Override serves as an example in this post. This
>>> action shall override a field in a message.
>>
>>One cannot override a field. Presently Ada's inheritance model is a type
>>extension one. I.e. you can add a field, but not replace it with another.
> 
> No! I was unclear. I'm not trying to override anything in that sense.
> 
> The messages are basically records filled with values. It's the
> _values_ in the existing message structures the Action.Override is
> supposed to override:
> 
> type Instance is new Message.Instance with
> record
> A : Float;
> B : Integer;
> ...
> end record;
> ...
> 
> Somewhere some mechanism assigns the initial values:
> M1 : Message.M1.Instance;
> Put_A(M1, 1.0);
> Put_B(M1, 1);
> ...
> 
> Later on, an override action shall be able to override these values by
> basically doing:
> 
> Put_A(M1, Some_Other_Value_Stored_In_The_Action);
> 
> again!

And why: M1.A := 10; is bad?

> Other messages may record the entire message and send it to some place
> else, or just extract some specific data field and pass along etc.
> 
>>> All actions are created and then enqueued in certain action queues in a
>>> preparation phase and then executed (real-time) in the execution phase.
>>> The messages are both created and transported through the system in the
>>> execution phase.
>>
>>> In order to store the value to override with, and where to put it when
>>> performing the override, the Action.Override stores an override
>>> argument (like a Float for instance) and an access to the proper
>>> Put-procedure (like Put_A(M : in out Message.M1.Instance; A : Float))
>>> for a certain message class instance (the specific message the action
>>> shall operate upon). The Put procedure takes as arguments the message
>>> instance and a value to put. The procedure name defines what message
>>> field to put the value into, such as Put_A for field A.
>>
>>It is too late. Basically Put should be a primitive operation of
>>Message...Instance. But when you pass a type as a formal generic parameter
>>it is already frozen, which in other words means that you cannot define a
>>primitive Put for it. You can do instead:
>>
>>generic
>>  type ArgumentType is private; -- Type extension
>>package Message.Extender is
>>  type Instance is new Message.Instance with record
>>     Extension : ArgumentType;
>>  end record;
>>  procedure Put (M : Instance; A : ArgumentType);
>>     -- This is a "method" of Instance now
>>
>>procedure Put (M : Instance; A : ArgumentType) is
>>begin
>>  M.Extension := A;
>>end Put;
> 
> You're saying I should replace every single field in every message
> record with an extension as above?
> 
> I sort of get what you mean, but not entirely. I can't see how my
> "complex" message records would look like. Each message consists of
> many variables and sub-records. I don't think this is what I look for.

ArgumentType can be a large record, array. That's no problem.

> It feels like you're answering another question here... maybe due to
> the above misunderstanding?

Yes. I didn't understand why do you need a generic package
Message.Override.

>>> The type of the field to override and the type of the message are
>>> generic parameters in the Action.Override package/class, so there's one
>>> instantiation of Action.Override for each message type and override type:
>>
>>> package M1_Float_Override_Action is new Action.Override
>>> (ArgumentType => Float,
>>> MessageType  => Message.M1.Instance);
>>
>>> There's another abstract parent-class, Message, and non-abstract child
>>> classes Message.M1, Message.M2 etc.
>>
>>It is not clear what are you going to achieve by that. It will not be
>>dynamically polymorphic anyway as long as ArgumentTypes are not derived
>>from the same base. So any possible gain = 0, IMO.
> 
> Correct. No polymorphy (at least not with this action-stuff). But all
> messages has common data and behaviours that I handle in the abstract
> base-class. So there's the gain.

Messages yes, but not the ArgumentTypes. I still do not follow you.

>>> Each child message has its own set of, non-virtual, Put procedures that
>>> writes the contents of the message. The message contents is a record
>>> with arbitrary width, depth and types.
>>
>>If Put is not dispatching, what the use of it. In particular, why ":=" does
>>not fit?
> 
> I want to assign values to fields in the messages from the override
> actions. How could this be done with ":=" only? I will not implement an
> action for EACH field in EACH message.

But there is no difference! ":=" is as good as "Put", two characters
shorter. For the language there is no difference.

> I would love to make Put dispatching but how would a procedure look
> like that can assign a value to any field in a record?

Variant 1. Derive all values from one base

   type Message_Value is abstract tagged ... or Controlled

Variant 2. Use streams. Your record will be a stream (derived from
Ada.Streams.Root_Stream_Type). For each type there are attributes 'Input
and 'Output. See ARM 13.13.

> (Yes, I'm still stuck with that record...)
> 
>>> There will also be a nice general mechanism that basically gets any
>>> type of message, applies any actions enqueued for it and then puts the
>>> message away. It feels like this should be possible with Ada mechanisms
>>> such as inheritage, polymorphy and generics but, I kind of doubt it
>>> now...
>>
>>It is definitely possible:
>>
>>package Messages is
>>  type Message is
>>     abstract new Ada.Finalization.Limited_Controlled with private;
>>  procedure Execute (This : in out Message) is abstract;
>>
>>  type Messages_Queue is
>>     [abstract] new Ada.Finalization.Limited_Controlled with private;
>>  -- Abstract if there should be different implementations of queues
>>  procedure Enqueue
>>     (  Queue : in out Messages_Queue;
>>        Item : in out Message'Class
>>     )  [is abstract];
>>private
>>  ...
>>end Messages;
>>
>>package Messages.My_Messages is
>>  type My_Message is new Message with private;
>>  procedure Finalize (This : in out Message); -- How to destruct
>>  procedure Initialize (This : in out Message); -- How to construct
>>  procedure Execute (This : in out Message); -- What to be done
>>  procedure Put_A (This : in out Message; A : Something);
>>  ...
> 
> Ugh, I don't get it.
> 
> How could the message know what to do in Execute? There may be 0 or 5
> or 500 actions to be taken on each message.

Ok, then replace "Message" with "Action" and make Message an Action
container.

> The messages do not exist
> when the actions are defined and hence can not be prepared with that
> information. Where/how do you store the values to override with for
> example...?

In the message (OK in the action (:-))

type My_Action is new Action with record
   ...- My precious values
end record;

[ Or should it be sort of C's union? Then you should clearly distinguish
the issue of memory allocation and reuse from the type hierarchy. In Ada
for the latter there are variant records and tagged types. For the former
you can use streams and memory pools. ]

> Maybe this too is an effect of the initial misunderstanding?
> 
> But I was enlighted by some other stuff in your example though :)
> 
>>> Code
>>[...]
>>
>>> 1. Why does this not work?
>>
>>The expected type is MessageType as the compiler tells. MessageType is a
>>generic formal type. The compiler does not know of any its possible
>>relationships with Message.Instance. It is just a type:
>>
>>generic
>>  type MessageType is private;
>>
>>It is not even tagged.
> 
> I see.
> 
> But the dispatching call "Message.Print(M.all)" works and that
> procedure expects as a parameter a concrete instance within
> Message.Instance'Class. It too gets this unknown MessageType yet the
> compiler doesn't whine.
> 
> What's the difference?

The difference in the expected type. Print expects a message,
OverrideMethodRefType expects MessageType. Those are different types. The
compiler does not know about your intention to substitute message for
MessageType. Because, next day, you might change your mind and substitute
Boolean instead.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  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-24 18:52 ` Nick Roberts
  2005-01-03 16:59   ` per
  2005-01-10 12:10   ` per
  2005-01-10 21:54 ` Simon Wright
  3 siblings, 2 replies; 31+ messages in thread
From: Nick Roberts @ 2004-12-24 18:52 UTC (permalink / raw)


"per" <commander@death-star.com> wrote:

From what I can gather about the design you are looking for, my impression
is that you need two tagged types -- one for messages, one for actions --
and a mechanism of 'multiple dispatch' to select the appropriate Put
procedure.

Some languages support a form of method selection called 'multiple
selection', where a method can be selected depending on the type of not just
one but several parameters. Multiple dispatch is the workmanlike way that
you do the equivalent of multiple selection in languages that don't support
it (such as Ada). The essence is that first you dispatch selecting on one
kind of tagged type (in this case, a message), into a procedure that then
itself dispatches selecting on another tagged type (in this case, an
action), and possibly so on for yet more parameters.

I'll try to illustrate this idea.

   with AI302.Indefinite_Double_Linked_Lists; -- for private part only
   package Messaging is

      type Message is abstract tagged limited private;
      type Action  is abstract tagged private;

      procedure Execute (M: in out Message;
                         A: in     Action'Class) is abstract;

      procedure Enqueue (M: in out Message'Class;
                         A: in     Action'Class);

      procedure Execute_Actions (M: in out Message'Class);

      Unsupported_Action: exception; -- message could not execute action
      Execution_Error: exception; -- something went wrong in Execute_Actions

   private
      type Action is abstract tagged limited null record;

      package Action_Queueing is new
         AI302.Indefinite_Doubly_Linked_Lists( Action'Class );
      subtype Action_Queue is Action_Queueing.Vector;

      type Message is abstract tagged limited
         record
            Queue: Action_Queueing.Action_Queue;
         end record;

   end Messaging;

   package body Messaging is

      use Action_Queueing;

      procedure Enqueue (M: in out Message'Class;
                         A: in     Action'Class) is
      begin
         Append( M.Queue, A );
      end;

      procedure Execute_Actions (M: in out Message'Class) is
         C: Action_Queueing.Cursor_Type := First(M.Queue);
         Error: Boolean := False;
      begin
         while C /= Action_Queueing.No_Element loop
            begin
               Execute( M, Element(M.Queue,C) ); -- dispatching
            exception
               when others => Error := True; -- note if something failed
            end;
            C := Next(C);
         end loop;
         Clear(M.Queue);
         if Error then raise Execution_Error; end if;
      end;

   end Messaging;

   package Messaging.Scoring is

      type Scoring_Action is new Action with
         record
            Score: Float;
         end;

      Minimum_Score: constant Float := 0.1;

   end;

   package Messaging.Hierarchy is

      type Hierarchy_Action is new Action with
         record
            Level: Natural;
         end;

   end;

   package Messaging.Stock_Movement is

      type Stock_Arrival_Message  is new Message with private;
      type Stock_Despatch_Message is new Message with private;

      procedure Execute (M: in out Stock_Arrival_Message;
                         A: in     Action'Class);

      procedure Execute (M: in out Stock_Despatch_Message;
                         A: in     Action'Class);

      Stock_Item_Error: exception; -- not a current stock item

   private
      type Stock_Movement_Message is new Message with
         record
            Item: Stock_ID;
         end record;

      type Stock_Arrival_Message is new Stock_Movement_Message with 
         record
            ...
         end record;

      type Stock_Despatch_Message is new Stock_Movement_Message with
         record
            ...
         end record;

   end;

   with Scoring, Hierarchy;
   package body Messaging.Stock_Movement is

      procedure Check_ID (M: in out Stock_Movement_Message'Class) is
      begin
         if not Is_Current(M.Item) then
            raise Stock_Item_Error;
         end if;
      end Check_ID;

      procedure Execute (M: in out Stock_Arrival_Message;
                         A: in     Action'Class) is
      begin
         Check_ID( M );
         if A in Scoring.Scoring_Action'Class then
            declare
               use Scoring;
               SA: constant Scoring_Action := Scoring_Action(A);
            begin
               if SA.Score < Minimum_Score then
                  ...
               else
                  ...
               end if;
            end;
         elsif A in Hierarchy.Hierarchy_Action'Class then
            ...
         else
            raise Unsupported_Action;
         end if;
      end Execute; -- for Stock_Arrival_Message

      procedure Execute (M: in out Stock_Despatch_Message;
                         A: in     Action'Class) is
      begin
         Check_ID( M );
         if A in Scoring.Scoring_Action'Class then
            ...
         elsif A in Hierarchy.Hierarchy_Action'Class then
            ...
         else
            raise Unsupported_Action;
         end if;
      end Execute; -- for Stock_Despatch_Message

   end Messaging.Stock_Movement;

The idea here is that the Execute procedure dispatches (singly) on a type
derived from Message, and within the body of any such Execute procedure, we
select again based on the actual type of the action.

In fact, to select again, we have to resort to the old fashioned technique
of a series of 'if' statements. This is because we cannot know in advance
all the possible types derived from Action (because new types could be added
after we have compiled), and Ada demands that all possible tags are known to
perform dynamic dispatch (so that there is no danger of jumping to a
procedure that is not there, and expensive tests can be avoided). If an
action is of a type we do not recognise, we raise an exception
(Unsupported_Action). Note the form of the tests ("if A in X'Class then
...") and the convenient conversion so that we can easily refer to specific
components (e.g. SA.Score).

Both new actions and new messages can be added. However, if a new action is
added, it might be necessary to modify the implementations of some (or all)
of the existing messages to support it.

Please study the code I've written carefully. However, I've not tested it in
any way, so it's likely to have some faults! I'm hoping that I've got close
here to providing a design that will be of genuine use to you, Per. If not,
please say! You will probably have a lot of questions, but that's fine by
me.

I've tried to demonstrate how it is often more appropriate to declare more
than one tagged type in a particular package, if they are closely related.
I've done this twice in my example above. In Messaging, I've declared
Message and Action. In Messaging.Stock_Movement, I've declared both
Stock_Arrival_Message and Stock_Despatch_Message, which share a common
private subancestor (Stock_Movement_Message) with common data and a private
operation of it own (Check_ID).

I've used Matthew Heaney's AI-302 implementation of containers for the queue
implementation, available at:

   http://home.earthlink.net/~matthewjheaney/charles/ai302-20040227.zip

Note that the Ada 2005 standard, when it is published, is likely to have
some minor differences to this package.

-- 
Nick Roberts



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2004-12-22 18:02     ` Martin Krischik
@ 2005-01-03 10:05       ` per
  0 siblings, 0 replies; 31+ messages in thread
From: per @ 2005-01-03 10:05 UTC (permalink / raw)


>We access the usenet directly with a usenet reader and not via a web
>browser.

Ok, too bad my company won't allow that.




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2004-12-22 19:54     ` Dmitry A. Kazakov
@ 2005-01-03 12:37       ` per
  2005-01-03 14:14         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: per @ 2005-01-03 12:37 UTC (permalink / raw)


Dmitry:

>> Somewhere some mechanism assigns the initial values:
>> M1 : Message.M1.Instance;
>> Put_A(M1, 1.0);
>> Put_B(M1, 1);
>> ...

>> Later on, an override action shall be able to override these values
by
>> basically doing:

>> Put_A(M1, Some_Other_Value_Stored_In_The_Action);

>> again!

>And why: M1.A := 10; is bad?

Hm.

1. Action does not know of the internals of M1 (such as that M1.A
exists)
2. Action should cope with any Message (M1, M2, etc).
3. Action should cope with any type of the field to overwrite.
4. The value to overwrite _with_ is determined at run-time (by the
user).

But I really don't care if I write ":=" or "Put_"; that's not the
issue.

I just want to be able to call a specific Put_... (or do ":=") on _any_
message with the appropriate arguments (message and field value)
determined at run-time (by the user).

Argh, I thought Ada would be a blessing. That old C construct 'void *'
should have been handy...
...

>> I would love to make Put dispatching but how would a procedure look
>> like that can assign a value to any field in a record?

>Variant 1. Derive all values from one base

>  type Message_Value is abstract tagged ... or Controlled

How would that make Put dispatching? Note that the Message is a fixed
record, whose fields (one or more) I want to assign one or more values
to (not known at compile time etc etc).

>Variant 2. Use streams. Your record will be a stream (derived from
>Ada.Streams.Root_Stream_Type). For each type there are attributes
'Input
>and 'Output. See ARM 13.13.

That's new stuff to me, have to check it out.

...

>>>> There will also be a nice general mechanism that basically gets
any
>>>> type of message, applies any actions enqueued for it and then puts
the
>>>> message away. It feels like this should be possible with Ada
mechanisms
>>>> such as inheritage, polymorphy and generics but, I kind of doubt
it
>>>> now...
>>>It is definitely possible:
>>>package Messages is
>>> type Message is
>>>     abstract new Ada.Finalization.Limited_Controlled with private;
>>> procedure Execute (This : in out Message) is abstract;

>>> type Messages_Queue is
>>>     [abstract] new Ada.Finalization.Limited_Controlled with
private;
>>> -- Abstract if there should be different implementations of queues
>>> procedure Enqueue
>>>     (  Queue : in out Messages_Queue;
>>>        Item : in out Message'Class
>>>     )  [is abstract];
>>>private
>>> ...
>>>end Messages;

>>>package Messages.My_Messages is
>>>  type My_Message is new Message with private;
>>>  procedure Finalize (This : in out Message); -- How to destruct
>>>  procedure Initialize (This : in out Message); -- How to construct
>>>  procedure Execute (This : in out Message); -- What to be done
>>>  procedure Put_A (This : in out Message; A : Something);
>>> ...

>> Ugh, I don't get it.

>> How could the message know what to do in Execute? There may be 0 or
5
>> or 500 actions to be taken on each message.

>Ok, then replace "Message" with "Action" and make Message an Action
>container.

This doesn't make sense to me (probably due to that I haven't
understood your idea).

My gut tells my that Messages should not "contain" Actions or vice
versa.

They are separate structures that should interract but not
"contaminate" each other.

Messages are separate from Actions, have separate lives and flow in the
system. Actions (may) appear in one part of the system and should be
"performed on" or "applied to" Messages, but not "owned by" or
"contained within" the Messages.

Message instances are created, routed and destroyed (sort of)
dynamically.
Actions are sitting in their queues waiting for the right moment
(user-defined time) to Execute. At that time a certain Message instance
exist on which type the Action is programmed to Execute upon. It
Executes (in this case overwrites a field in the message record with a
user-defined value), then the Action is destroyed. The Message is
passed on to other adventures (such as being acted upon by other
Actions)

The life time of Messages and Actions are hence fundamentally
different. I'm afraid we don't understand eachother. Probably due to
that I, being an Ada newbie, try to grasp too much at one time and also
fail in explaining the problem.

>The difference in the expected type. Print expects a message,
>OverrideMethodRefType expects MessageType. Those are different types.
The
>compiler does not know about your intention to substitute message for
>MessageType. Because, next day, you might change your mind and
substitute
>Boolean instead.

OK, I can see that now.




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-03 12:37       ` per
@ 2005-01-03 14:14         ` Dmitry A. Kazakov
  2005-01-04 12:05           ` per
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-03 14:14 UTC (permalink / raw)


On 3 Jan 2005 04:37:03 -0800, per wrote:

>>> Somewhere some mechanism assigns the initial values:
>>> M1 : Message.M1.Instance;
>>> Put_A(M1, 1.0);
>>> Put_B(M1, 1);
>>> ...
> 
>>> Later on, an override action shall be able to override these values by
>>> basically doing:
> 
>>> Put_A(M1, Some_Other_Value_Stored_In_The_Action);
> 
>>> again!
> 
>>And why: M1.A := 10; is bad?
> 
> Hm.
> 
> 1. Action does not know of the internals of M1 (such as that M1.A exists)

Neither it knows that M1 has Put. Again there is no difference.

> 2. Action should cope with any Message (M1, M2, etc).

So they have to have something in common. What? The name "Put"? It is too
little and as good as ":=". 

> 3. Action should cope with any type of the field to overwrite.

What is known about *any* type? Nothing. So what can action do with that?
Also nothing.

> 4. The value to overwrite _with_ is determined at run-time (by the user).

Value or type. It is a great difference. A value determined at run-time?
Here:

   I : Integer;
begin
   I := <determinied at run-time value>;

A type determined at run-time? That is either class-wide or variant record.

> But I really don't care if I write ":=" or "Put_"; that's not the issue.
> 
> I just want to be able to call a specific Put_... (or do ":=") on _any_
> message with the appropriate arguments (message and field value)
> determined at run-time (by the user).

Appropriate arguments constitute no language entity. Specific Put means
that Put is polymorphic. It means that there is *one* Put, implemented by
many specific overridings of Put according to the concrete type of a
polymorphic (class-wide) parameter. But it is *one* Put, with *one*
parameter profile, of *one* root type.

> Argh, I thought Ada would be a blessing. That old C construct 'void *'
> should have been handy...

Observe that void * is a type. Exactly one type. You cannot achieve what
you describe with void *. It is rather C++'s ellipsis ".." what you are
talking about. But neither ".." nor "void *" arguments cannot be
dispatching. You have to bring *all* possible variants under one roof.
There are many ways to do it. 

>>> I would love to make Put dispatching but how would a procedure look
>>> like that can assign a value to any field in a record?
> 
>>Variant 1. Derive all values from one base
> 
>>  type Message_Value is abstract tagged ... or Controlled
> 
> How would that make Put dispatching? Note that the Message is a fixed
> record, whose fields (one or more) I want to assign one or more values
> to (not known at compile time etc etc).

type Message_Value is abstract tagged;
procedure Put (Action : Action_Type'Class, Value : Message_Value);
   -- This dispatches on Value

>>> How could the message know what to do in Execute? There may be 0 or 5
>>> or 500 actions to be taken on each message.
> 
>>Ok, then replace "Message" with "Action" and make Message an Action container.
> 
> This doesn't make sense to me (probably due to that I haven't
> understood your idea).
> 
> My gut tells my that Messages should not "contain" Actions or vice versa.
> 
> They are separate structures that should interract but not
> "contaminate" each other.
> 
> Messages are separate from Actions, have separate lives and flow in the
> system. Actions (may) appear in one part of the system and should be
> "performed on" or "applied to" Messages, but not "owned by" or
> "contained within" the Messages.

Who determines what action to apply? Above you talked about actions dealing
with polymorphic messages. That means that the message type (its type tag
buried in a class-wide value) determines the action. Is that correct? If
yes, then definitely actions are just methods of Message. What is Execute
then?

> Message instances are created, routed and destroyed (sort of) dynamically.
> Actions are sitting in their queues waiting for the right moment
> (user-defined time) to Execute. At that time a certain Message instance
> exist on which type the Action is programmed to Execute upon. It
> Executes (in this case overwrites a field in the message record with a
> user-defined value), then the Action is destroyed. The Message is
> passed on to other adventures (such as being acted upon by other
> Actions)

So message is a polymorphic value. This is the variant 1. If message may
contain more than one value then it is a container of Message_Value.

> The life time of Messages and Actions are hence fundamentally
> different. I'm afraid we don't understand eachother. Probably due to
> that I, being an Ada newbie, try to grasp too much at one time and also
> fail in explaining the problem.

Try to explain it in C++ terms. Draw a class diagram. What are
relationships between Message, Action, Execute?

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2004-12-24 18:52 ` Nick Roberts
@ 2005-01-03 16:59   ` per
  2005-01-10 12:10   ` per
  1 sibling, 0 replies; 31+ messages in thread
From: per @ 2005-01-03 16:59 UTC (permalink / raw)


Nick:

>From what I can gather about the design you are looking for, my
impression
>is that you need two tagged types -- one for messages, one for actions
--
>and a mechanism of 'multiple dispatch' to select the appropriate Put
>procedure.

Thanks for joining in Nick.

Yes, I believe your impression is right! Unfortunately my problem lies
a little deeper than that I think...

>Some languages support a form of method selection called 'multiple
>selection', where a method can be selected depending on the type of
not >just
>one but several parameters. Multiple dispatch is the workmanlike way
that
>you do the equivalent of multiple selection in languages that don't
>support
>it (such as Ada). The essence is that first you dispatch selecting on
one
>kind of tagged type (in this case, a message), into a procedure that
then
>itself dispatches selecting on another tagged type (in this case, an
>action), and possibly so on for yet more parameters.

OK, I get the idea and it could be half-way to what I want.

I just have to add that I (and probably you too) really dislike that
kind of "manual dispatching" for obvious reasons (maintenance, risk of
error, the volume of work if there are lots of Actions/Messages, etc).

>I'll try to illustrate this idea.

You did!

Actually Nick, parts of your example is very similar to my code!

There are some things I don't think will work in my system (such as
letting the Actions reside in queues _inside_ a Message, see my post
above), but let's leave that for the moment...

>    procedure Execute (M: in out Stock_Despatch_Message;
>                       A: in     Action'Class) is

My declaration of procedure "Execute" (declared for each Action
package):

procedure Execute(Self : in out Instance;
M    : in     Message.Class_Reference);

is very similar to your.

Rewritten to

procedure Execute(Self : in out Instance;
M    : in     Message.Instance'Class);

it is almost identical. I have switched the "dispatching order" since I
want to do Execute on an Action and not a Message (and my design
currently is that way), but in principle that doesn't matter, right?

If I modify the body of Action.Override.Execute to something like this:

procedure Execute(Self : in out Instance;
M    : in     Message.Instance'Class) is

begin
--     Message.Print(M.all);
if M in Message.M1.Instance'Class then
...
elsif M in Message.M2.Instance'Class then
...
elsif M in <and so on...>
...
end;
end;

I have your manual dispatching, right? So far so good.

Now to the actual problem:

What should I write in the if-clauses?

One "obvious" variant is:

if M in Message.M1.Instance'Class then
Self.OverrideMethodRef(Message.M1.Instance(M), Self.Argument);
...

(This is supposed to include an explicit type conversion from
Message.Instance'Class to Message.M1.Instance.)

But that won't compile since the compiler expects MessageType (generic
parameter) but gets Message.M1.Instance.

(Self.OverrideMethodRef is a procedure-access to a Put_ procedure with
a specific message and a value as parameter.)

And this is the very same problem I had from the beginning! :(

>Please study the code I've written carefully. However, I've not tested
it >in
>any way, so it's likely to have some faults! I'm hoping that I've got
>close
>here to providing a design that will be of genuine use to you, Per. If
>not,
>please say! You will probably have a lot of questions, but that's fine
by
>me.

I think I've got your point, although I still have problems as you see.
Do you have any further ideas?




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-03 14:14         ` Dmitry A. Kazakov
@ 2005-01-04 12:05           ` per
  2005-01-04 13:30             ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: per @ 2005-01-04 12:05 UTC (permalink / raw)


Dmitry, I think the misunderstandings has reached a point where I need
to restart.

I'll make a last attempt to explain the problem (although time is
running and I'll probably move on to another approach).

I want to enable a user to schedule actions to be performed att certain
system ticks on messages that flows in a system. Each message is
basically a record.

One sort of action is the "Override" action. This action shall put one
user-defined value into a user-selected field inside the message
record. The type of the value is arbitrary, but is known att
compile-time. The value itself is only known in run-time and is
provided by the user in an initialization phase. Many many action may
be scheduled to execute on many or the same message.

Later, the system enters the execution phase: For each system tick, all
Actions that are active in that particular tick, are executed upon the
all current message instances (only if there exist actions for that
type of message of course).

There are also other actions such as Record, Error etc, that do other
stuff (indicated by their names). I leave these actions out in this
description.

Messages are separate from Actions and are declared in separate
packages (probably in separate directories and perhaps by different
developers).

Class hierachy (I hope ASCII graphics will work, but google seems to
throw away spaces...):

Message
|
---------------------------
|        |        |
M1       M2       ...

Action
|
------------------------------------
|        |        |        |
Override Record   Error    ...

Or with plain text just in case:
Message
Message.M1
Message.M2

Action
Action.Override
Action.Record
Action.Error


Details of the classes:

Message
==============
Attributes
--------------
Id : Integer;
...

Operations
--------------
Put_Id(This : Message'Class; New_Id : Integer);
Get_Id(This : Message'Class) return Integer;
...

Message.M1
==============
Attributes
--------------
A : Integer;
B : Float;

Operations
--------------
Put_A(This : in out M1; New_A : in Integer);
Put_B(This : in out M1; New_B : in Float);


Message.M2
==============
Attributes
--------------
A : Float;
B : Float;
C : Natural;

Operations
----------
Put_A(This : in out M1; New_A : in Float);
Put_B(This : in out M1; New_B : in Float);
Put_C(This : in out M1; New_C : in Natural);

Here comes my attempt on Action (which doesn't work all the way):

Action
==============
Attributes
--------------
Tick : Integer;
...

Operations
--------------
Put_Tick(This : in out Action'Class; New_Tick : in Integer);
Get_Tick(This : in Tick'Class) return Integer;
Execute(This : in out Action; Message : in Message'Class);
...

Action.Override (generic)
==============
The class is generic to cope with any type of value to override AND to
store an access to any Put_-procedure connected to the message to
override values in.

generic
type ArgumentType is private;
type MessageType is private;

--The procedure-access type to the Put-procedure that performs the
actual overriding:
type OverrideMethodRefType is access procedure(M : in out MessageType;
A : in ArgumentType);

Attributes
--------------
Argument         : ArgumentType          -- The value to override with
("any" type)
OverrideMehodRef : OverrideMethodRefType -- The procedure to call with
the argument in order to override (the specific Put-procedure)

Operations
--------------
Initialize(This      : in out Action.Override;
Tick      : Integer;
Argument  : ArgumentType;
MethodRef : OverrideMethodRefType)
begin
...
This.ARgument := Argument;
This.OverrideMethodRef := MethodRef;
...
end;

Execute(This : in out Action.Override; Message : in Message'Class);
begin
-- The actual overriding:
This.OverrideMethodRef(Message, This.Argument);
end;

The contents of the attributes ARgument and OverrideMethodRef is
assigned in procedure Initialize during the initialization phase.

I've left out all stuff about queues and such...

FYI:
In C I would have stored a pointer to a Put-function, looking something
like "void M1_Put_A(void * M1, void * Argument)", in OverrideMethodRef.
All Put functions would have had identical signatures such as (void *
Message, void * Argument). Each specific Put-function would make its
own interpretation of Message and Argument (after all they are specific
and knows how to interpret both arguments).

My Ada solution may be influenced on my experiences with C but I'm
looking for (or was...) the Ada way to do it, which is why I started
this topic in the first place.

Until someone comes up with an elegant Ada solution to this I'll go for
another approach.

Phew!




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-04 12:05           ` per
@ 2005-01-04 13:30             ` Dmitry A. Kazakov
  2005-01-04 15:21               ` per
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-04 13:30 UTC (permalink / raw)


On 4 Jan 2005 04:05:26 -0800, per wrote:

> Dmitry, I think the misunderstandings has reached a point where I need
> to restart.
> 
> I'll make a last attempt to explain the problem (although time is
> running and I'll probably move on to another approach).
> 
> I want to enable a user to schedule actions to be performed att certain
> system ticks on messages that flows in a system. Each message is
> basically a record.
> 
> One sort of action is the "Override" action. This action shall put one
> user-defined value into a user-selected field inside the message
> record. The type of the value is arbitrary, but is known att
> compile-time. The value itself is only known in run-time and is
> provided by the user in an initialization phase. Many many action may
> be scheduled to execute on many or the same message.
> 
> Later, the system enters the execution phase: For each system tick, all
> Actions that are active in that particular tick, are executed upon the
> all current message instances (only if there exist actions for that
> type of message of course).
> 
> There are also other actions such as Record, Error etc, that do other
> stuff (indicated by their names). I leave these actions out in this
> description.
> 
> Messages are separate from Actions and are declared in separate
> packages (probably in separate directories and perhaps by different
> developers).
> 
> Class hierachy (I hope ASCII graphics will work, but google seems to
> throw away spaces...):
> 
> Message
>|
> ---------------------------
>|        |        |
> M1       M2       ...
> 
> Action
>|
> ------------------------------------
>|        |        |        |
> Override Record   Error    ...
> 
> Or with plain text just in case:
> Message
> Message.M1
> Message.M2
> 
> Action
> Action.Override
> Action.Record
> Action.Error
> 
> 
> Details of the classes:
> 
> Message
> ==============
> Attributes
> --------------
> Id : Integer;
> ...
> 
> Operations
> --------------
> Put_Id(This : Message'Class; New_Id : Integer);
> Get_Id(This : Message'Class) return Integer;
> ...
> 
> Message.M1
> ==============
> Attributes
> --------------
> A : Integer;
> B : Float;
> 
> Operations
> --------------
> Put_A(This : in out M1; New_A : in Integer);
> Put_B(This : in out M1; New_B : in Float);
> 
> 
> Message.M2
> ==============
> Attributes
> --------------
> A : Float;
> B : Float;
> C : Natural;
> 
> Operations
> ----------
> Put_A(This : in out M1; New_A : in Float);
> Put_B(This : in out M1; New_B : in Float);
> Put_C(This : in out M1; New_C : in Natural);
> 
> Here comes my attempt on Action (which doesn't work all the way):
> 
> Action
> ==============
> Attributes
> --------------
> Tick : Integer;
> ...
> 
> Operations
> --------------
> Put_Tick(This : in out Action'Class; New_Tick : in Integer);
> Get_Tick(This : in Tick'Class) return Integer;
> Execute(This : in out Action; Message : in Message'Class);
> ...
> 
> Action.Override (generic)
> ==============
> The class is generic to cope with any type of value to override AND to
> store an access to any Put_-procedure connected to the message to
> override values in.
> 
> generic
> type ArgumentType is private;
> type MessageType is private;
> 
> --The procedure-access type to the Put-procedure that performs the
> actual overriding:
> type OverrideMethodRefType is access procedure(M : in out MessageType;
> A : in ArgumentType);
>
> Attributes
> --------------
> Argument         : ArgumentType          -- The value to override with
> ("any" type)
> OverrideMehodRef : OverrideMethodRefType -- The procedure to call with
> the argument in order to override (the specific Put-procedure)
> 
> Operations
> --------------
> Initialize(This      : in out Action.Override;
> Tick      : Integer;
> Argument  : ArgumentType;
> MethodRef : OverrideMethodRefType)
> begin
> ...
> This.ARgument := Argument;
> This.OverrideMethodRef := MethodRef;
> ...
> end;
> 
> Execute(This : in out Action.Override; Message : in Message'Class);
> begin
> -- The actual overriding:
> This.OverrideMethodRef(Message, This.Argument);
> end;
> 
> The contents of the attributes ARgument and OverrideMethodRef is
> assigned in procedure Initialize during the initialization phase.
> 
> I've left out all stuff about queues and such...

OK, try this:

generic
   type Argument_Type is private;
   type Message_Type is new Message with private;
   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
         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) &
         );
      end if;
      Put (Message_Type'Class (Message), This.Argument);
   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
       );

> FYI:
> In C I would have stored a pointer to a Put-function, looking something
> like "void M1_Put_A(void * M1, void * Argument)", in OverrideMethodRef.
> All Put functions would have had identical signatures such as (void *
> Message, void * Argument). Each specific Put-function would make its
> own interpretation of Message and Argument (after all they are specific
> and knows how to interpret both arguments).

You do not need all that stuff with pointers because the package is already
generic. So you can specify the Put procedure you need, during
instantiation. It won't work with other types of arguments and messages
anyway.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-04 13:30             ` Dmitry A. Kazakov
@ 2005-01-04 15:21               ` per
  2005-01-04 17:47                 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: per @ 2005-01-04 15:21 UTC (permalink / raw)


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...?

>  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"?

>        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);

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?

>  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...

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?




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-04 15:21               ` per
@ 2005-01-04 17:47                 ` Dmitry A. Kazakov
  2005-01-05 12:01                   ` per
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-04 17:47 UTC (permalink / raw)


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



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-04 17:47                 ` Dmitry A. Kazakov
@ 2005-01-05 12:01                   ` per
  2005-01-05 13:23                     ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: per @ 2005-01-05 12:01 UTC (permalink / raw)


Dmitry, guess what, your solution helped me make my *first* solution
(with access to Put-procedures) work!

The key point was the contract on generic Message_Type and the type
conversion in Execute:

generic
type Message_Type is new Message with private;

...

procedure Execute(Self : in out Instance;
M    : in Message.Class_Reference) is
begin
This.OverrideMethodRef(Message_Type(M.all), Self.Argument);
end;

It may not be the most elegant Ada-ish solution, but my initial idea
works and that's encouraging at least.

Dmitry:

...

>>>     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!

It did :)

...

>> 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.)

>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);

?

...

>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.

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?

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.

>----
>* If action is not a descendant of message. This is why I tried to
bring
>you to this decision, but you were adamant. (:-))

Hm, they keep saying that... ;)

Also have in mind that I may fail in digesting everything you say...
I'm both an Ada beginner and increasingly stressed... I've spent too
much time on this problem by now. But you've been of GREAT help!




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-05 12:01                   ` per
@ 2005-01-05 13:23                     ` Dmitry A. Kazakov
  2005-01-05 15:59                       ` per
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-05 13:23 UTC (permalink / raw)


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



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-05 13:23                     ` Dmitry A. Kazakov
@ 2005-01-05 15:59                       ` per
  2005-01-05 20:44                         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: per @ 2005-01-05 15:59 UTC (permalink / raw)


>> 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.

Ah, I get it.

In my example A and B were supposed to have different sematics.

The messages in this system are actually unknown right now. My system
shall simulate many different devices and their communication. The
message specifications are defined by each device vendor, not by us
(the team). Hence the message contents is quite unknown and I'd like to
cope with "anything". We can however implement the messages in any way
we like as long as we can convert it to some format that can be sent on
different hardware communication linkst in some defined way.

...

>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. (:-))

Well, each message *is* a simple container, more or less. The contents
are related in the sense they are to be sent to/from a certain device
that expects a defined set of information. The program we discuss
doesn't really care at all of the contents of each message, but shall
provide means to the user to override any message content (among other
things).

>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.

I kind of see what this aims at (although "mix-in" is new to me).

As far as I see we're back to defining one action type for each message
and each field in the message, although this time without the generics,
right?

Can an action, eg Override_A, exist without an M1 instance to "point"
at?

Action has a different life-cycle than Message.




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-05 15:59                       ` per
@ 2005-01-05 20:44                         ` Dmitry A. Kazakov
  2005-01-10  8:42                           ` per
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-05 20:44 UTC (permalink / raw)


On 5 Jan 2005 07:59:05 -0800, per wrote:

>>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. (:-))
> 
> Well, each message *is* a simple container, more or less. The contents
> are related in the sense they are to be sent to/from a certain device
> that expects a defined set of information. The program we discuss
> doesn't really care at all of the contents of each message, but shall
> provide means to the user to override any message content (among other
> things).

How do you plan to maintain data consistency of a message if the user may
arbitrarily override its fields? I would try to do it more OO. To think
about what can be done with a message. And consider action as an
implementation detail of a concrete type of messages. Then actions could
vanish. (Just a guess, I still can't tolerate both messages and actions in
one bottle. (:-))

>>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.
> 
> I kind of see what this aims at (although "mix-in" is new to me).
> 
> As far as I see we're back to defining one action type for each message
> and each field in the message, although this time without the generics,
> right?

Yes, generics do not help here much because the field name cannot be a
formal parameter.

> Can an action, eg Override_A, exist without an M1 instance to "point"
> at?

It cannot, this is the whole idea of access discriminants. They are never
null and cannot be changed. Otherwise one should use access types
(pointers).

> Action has a different life-cycle than Message.

That's no problem as long as its life in Message's one. For exampe:

declare
   M : aliased M1;
begin
   ...
   declare
      A : Override_A (M'Access);
   begin
      ... -- The scope of the action A is nested
   end;
   ...
end;

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-05 20:44                         ` Dmitry A. Kazakov
@ 2005-01-10  8:42                           ` per
  2005-01-10 14:22                             ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: per @ 2005-01-10  8:42 UTC (permalink / raw)


>How do you plan to maintain data consistency of a message if the user
may
>arbitrarily override its fields? I would try to do it more OO. To
think

Well, that's the user's problem. Or rather, that's the point, sort of.
See, this is a test equipment and shall enable modification of messages
in "any" way in order to test how the test object (a computer) reacts.

>about what can be done with a message. And consider action as an
>implementation detail of a concrete type of messages. Then actions
could
>vanish. (Just a guess, I still can't tolerate both messages and
actions in
>one bottle. (:-))
>
>> Can an action, eg Override_A, exist without an M1 instance to
"point"
>> at?
>
>It cannot, this is the whole idea of access discriminants. They are
never
>null and cannot be changed. Otherwise one should use access types
>(pointers).
>
>> Action has a different life-cycle than Message.
>
>That's no problem as long as its life in Message's one. For exampe:
>
>declare
>  M : aliased M1;
>begin
>  ...
>  declare
>     A : Override_A (M'Access);
>  begin
>     ... -- The scope of the action A is nested
>  end;
>  ...
>end;

How would you create a schedule of actions before any messages exist?
(Schedule = Plan of what actions should be taken to what type of
messages and when, created *before* any message instances exist)




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  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
  1 sibling, 1 reply; 31+ messages in thread
From: per @ 2005-01-10 12:10 UTC (permalink / raw)


Nick Roberts:

>I'll try to illustrate this idea.
>
>  with AI302.Indefinite_Double_Linked_Lists; -- for private part only
>  package Messaging is
...
>I've used Matthew Heaney's AI-302 implementation of containers for the
queue
>implementation, available at:
>
>  http://home.earthlink.net/~matthewjheaney/charles/ai302-20040227.zip

Nick, I can't find "Indefinite_Double_Linked_Lists" or similar anywhere
in Matthews package.

Should it be there ready-to-use or is it something I have to write
myself?




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-10 12:10   ` per
@ 2005-01-10 13:49     ` Marius Amado Alves
  0 siblings, 0 replies; 31+ messages in thread
From: Marius Amado Alves @ 2005-01-10 13:49 UTC (permalink / raw)
  To: comp.lang.ada

>> http://home.earthlink.net/~matthewjheaney/charles/ai302-20040227.zip
> 
> Nick, I can't find "Indefinite_Double_Linked_Lists" or similar anywhere
> in Matthews package.

Charles has moved to tigris.org sometime ago. (I don't know if the 
latest version includes IDLL.)




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-10  8:42                           ` per
@ 2005-01-10 14:22                             ` Dmitry A. Kazakov
  2005-01-10 16:24                               ` per
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-10 14:22 UTC (permalink / raw)


On 10 Jan 2005 00:42:44 -0800, per wrote:

>>How do you plan to maintain data consistency of a message if the user may
>>arbitrarily override its fields? I would try to do it more OO. To think
> 
> Well, that's the user's problem. Or rather, that's the point, sort of.
> See, this is a test equipment and shall enable modification of messages
> in "any" way in order to test how the test object (a computer) reacts.

The reaction could be "Access violation" (:-)) It is utterly untyped, or
non-OO, I should say. So it looks not very promising to to solve it using
OO. I still have an impression that you should return back to domain
analysis and find better objects than mysterious actions and messages. At
least there should be no place for the word "any".

>>about what can be done with a message. And consider action as an
>>implementation detail of a concrete type of messages. Then actions could
>>vanish. (Just a guess, I still can't tolerate both messages and actions in
>>one bottle. (:-))
>>
>>> Can an action, eg Override_A, exist without an M1 instance to "point"
>>> at?
>>
>>It cannot, this is the whole idea of access discriminants. They are never
>>null and cannot be changed. Otherwise one should use access types
>>(pointers).
>>
>>> Action has a different life-cycle than Message.
>>
>>That's no problem as long as its life in Message's one. For exampe:
>>
>>declare
>>  M : aliased M1;
>>begin
>>  ...
>>  declare
>>     A : Override_A (M'Access);
>>  begin
>>     ... -- The scope of the action A is nested
>>  end;
>>  ...
>>end;
> 
> How would you create a schedule of actions before any messages exist?
> (Schedule = Plan of what actions should be taken to what type of
> messages and when, created *before* any message instances exist)

So you schedule actions on something that even does not exist. It is a bit
strange. Normally scheduling depends on some state of *existing* scheduled
items. Probably this unconventional use of terms message and action is the
source of the misunderstanding. Aren't messages used to invoke actions?
Aren't scheduled actions invoked by messages from a scheduler?

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-10 14:22                             ` Dmitry A. Kazakov
@ 2005-01-10 16:24                               ` per
  2005-01-10 19:09                                 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 31+ messages in thread
From: per @ 2005-01-10 16:24 UTC (permalink / raw)


>>>How do you plan to maintain data consistency of a message if the
user may
>>>arbitrarily override its fields? I would try to do it more OO. To
think
>
>> Well, that's the user's problem. Or rather, that's the point, sort
of.
>> See, this is a test equipment and shall enable modification of
messages
>> in "any" way in order to test how the test object (a computer)
reacts.
>
>The reaction could be "Access violation" (:-)) It is utterly untyped,
or
>non-OO, I should say. So it looks not very promising to to solve it
using
>OO. I still have an impression that you should return back to domain
>analysis and find better objects than mysterious actions and messages.
At
>least there should be no place for the word "any".

Hehe, you never give up, do you? ;)

OK, "any way" = any way the message can be modified in:

If M1.A is an Integer, then the user shall be able to put any Integer
in M1.A (at a specified time in the message M1 that happen to exist at
that time).

A Message is a record and an Action is a way to manipulate the Message.
Not very mysterious to me.

>> How would you create a schedule of actions before any messages
exist?
>> (Schedule = Plan of what actions should be taken to what type of
>> messages and when, created *before* any message instances exist)
>
>So you schedule actions on something that even does not exist. It is a
bit
>strange. Normally scheduling depends on some state of *existing*
scheduled
>items.

I disagree. All message *types* are known. The message *instances*
flows through the system (having the time of their life so to speak;).
Some of them untouched, and some of them manipulated in a predefined
way. An action is that predefined way. And a bunch of actions
programmed to occur at certain times constitutes an action schedule.

>Probably this unconventional use of terms message and action is the
>source of the misunderstanding. Aren't messages used to invoke
actions?
>Aren't scheduled actions invoked by messages from a scheduler?

No, my messages and actions do not have that relationship. (I see no
absolute semantic in the terms Action and Message, that's why I've
tried to explain what they mean in this system.)

Actions are invoked from a scheduler. The actions are performed on the
instances that happen to exist (at the time each action is activated
and if there is an action for that message type).

Messages then go to different devices an mean whatever they are
supposed to these devices. The actions just do stuff with the messages
before they (the messages) are passed on.




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-10 16:24                               ` per
@ 2005-01-10 19:09                                 ` Dmitry A. Kazakov
  2005-01-11  9:06                                   ` per
  0 siblings, 1 reply; 31+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-10 19:09 UTC (permalink / raw)


On 10 Jan 2005 08:24:31 -0800, per wrote:

>>>>How do you plan to maintain data consistency of a message if the user may
>>>>arbitrarily override its fields? I would try to do it more OO. To think
>>
>>> Well, that's the user's problem. Or rather, that's the point, sort of.
>>> See, this is a test equipment and shall enable modification of messages
>>> in "any" way in order to test how the test object (a computer) reacts.
>>
>>The reaction could be "Access violation" (:-)) It is utterly untyped, or
>>non-OO, I should say. So it looks not very promising to to solve it using
>>OO. I still have an impression that you should return back to domain
>>analysis and find better objects than mysterious actions and messages. At
>>least there should be no place for the word "any".
> 
> Hehe, you never give up, do you? ;)

Almost (:-))

> OK, "any way" = any way the message can be modified in:
> 
> If M1.A is an Integer, then the user shall be able to put any Integer
> in M1.A (at a specified time in the message M1 that happen to exist at
> that time).

But "to put something into" cannot be considered as an operation of
something more complex than just an untyped container. This is a weakness
of your analysis. If message is more than a chunk of raw memory then "to
put" is an implementation of something more meaningful than just copy from
one memory location to another. Once you factor out that semantics you will
probably be able to get rid of public components A, B, C etc.

> A Message is a record and an Action is a way to manipulate the Message.
> Not very mysterious to me.

So replace it with:

Message is a type
Action is an operation on it.
Scheduled item is a task calling operations of a message.

>>> How would you create a schedule of actions before any messages exist?
>>> (Schedule = Plan of what actions should be taken to what type of
>>> messages and when, created *before* any message instances exist)
>>
>>So you schedule actions on something that even does not exist. It is a bit
>>strange. Normally scheduling depends on some state of *existing* scheduled
>>items.
> 
> I disagree. All message *types* are known.

But scheduling items are instances not types. Instances are unknown.

> The message *instances*
> flows through the system (having the time of their life so to speak;).
> Some of them untouched, and some of them manipulated in a predefined
> way. An action is that predefined way. And a bunch of actions
> programmed to occur at certain times constitutes an action schedule.

OK, why that bunch of actions cannot be just a piece of code in a task? Are
you writing an interpreter?

>>Probably this unconventional use of terms message and action is the
>>source of the misunderstanding. Aren't messages used to invoke actions?
>>Aren't scheduled actions invoked by messages from a scheduler?
> 
> No, my messages and actions do not have that relationship. (I see no
> absolute semantic in the terms Action and Message, that's why I've
> tried to explain what they mean in this system.)
> 
> Actions are invoked from a scheduler. The actions are performed on the
> instances that happen to exist (at the time each action is activated
> and if there is an action for that message type).
> 
> Messages then go to different devices an mean whatever they are
> supposed to these devices. The actions just do stuff with the messages
> before they (the messages) are passed on.

Shouldn't messages be formed rather in terms of device semantics? And why
should they exist before being sent to the devices? Is it a kind of
premature memory optimization?

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2004-12-22 13:49 OO problem: Performing actions on messages (very long, sorry) per
                   ` (2 preceding siblings ...)
  2004-12-24 18:52 ` Nick Roberts
@ 2005-01-10 21:54 ` Simon Wright
  3 siblings, 0 replies; 31+ messages in thread
From: Simon Wright @ 2005-01-10 21:54 UTC (permalink / raw)


"per" <commander@death-star.com> writes:

> I'm new to Ada (but not to OO). I'm having trouble designing a
> system, or at least a key part of it. (I have a few alternative
> non-tested solutions but I don't get why this doesn't work.) And I'd
> like to do it "by the book" in nice OO Ada coding style.

Nothing wrong with that, but you do need to get the right classes
(analyse the right problem? -- UML background coming out there!).

Seems to me you have a number of message specs; a message spec
consists of a (ordered) set of field specs; a field spec consists of a
type (and perhaps a size). It's here that you're going to hit "simple"
Ada types.

I'm not sure where your actions come in; are they related to a field
type, to a field in a message type, or a field in a particular
message?

Anyway, given a set of field specs in a message spec you can build up
a particular message (consisting of field _values_ corresponding to
the specs). And the action can do its thing when appropriate,

The message would (in my view) have a lot more to it than the bare
bytes that get sent to the equipment under test (I suspect you think
so too). What might be appropriate here is a convert-to-byte-stream
operation.

The advantage of a data-driven approach like this, if you can swing
it, is that adding new messages is only a matter of adding new message
type definitions, which are just data about messages.

Just my 2p --



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: OO problem: Performing actions on messages (very long, sorry)
  2005-01-10 19:09                                 ` Dmitry A. Kazakov
@ 2005-01-11  9:06                                   ` per
  0 siblings, 0 replies; 31+ messages in thread
From: per @ 2005-01-11  9:06 UTC (permalink / raw)


>But scheduling items are instances not types.

Yes. They are called Actions.

>Instances are unknown.

*Message* instances are unknown when the scheduling takes place.

>OK, why that bunch of actions cannot be just a piece of code in a
task?

The user is not supposed to code, build and load the entire system for
every test. He/she shall schedule actions appropriate to the current
test and run it the system, reschedule for another test and run it etc.
(Scripting in non-realtime is BTW done with Tcl/Tk & Tash.)

>Are you writing an interpreter?

Well, thinking about it - yes, I suppose.

>Shouldn't messages be formed rather in terms of device semantics? And
why
>should they exist before being sent to the devices? Is it a kind of
>premature memory optimization?

1. Device 1 delivers data into the system (which also can be looked
upon as a kind of router not caring too much of message contents)

2. The system intercepts the data (a Message) letting the user
manipulate, display, record etc the data according to a preprogrammed
schedule (created by the user, in form of a list of different Actions,
not hardcoded in Ada etc etc). In this sense the system must "know of"
the message contents.

3. The system delivers the data (Message) to Device 2.

Gentlemen, my problems are solved thanks to a hint in one of Dmitry's
earlier  examples. I'll now settle with that.
I thank you all for your efforts.




^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2005-01-11  9:06 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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

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