home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.lang.ada
- Path: sparky!uunet!noc.near.net!inmet!cobra!bwhite
- From: bwhite@cobra.camb.inmet.com (Bill White)
- Subject: Re: Ada 9x Dispatching Question
- Message-ID: <1992Aug17.215219.967@inmet.camb.inmet.com>
- Keywords: Ada Dispatching Polymorphism
- Sender: news@inmet.camb.inmet.com
- Nntp-Posting-Host: cobra
- Organization: Intermetrics Inc, Cambridge MA
- References: <334@sps.com>
- Date: Mon, 17 Aug 1992 21:52:19 GMT
- Lines: 136
-
- In response to Joe Tallet's recent note in comp.lang.ada:
-
- Dispatching only occurs when (1) the operation is an operation of a tagged
- type, and (2) the operand is of a class-wide type. It does not matter that
- the operation is obtained by an expanded name or by direct (use) visibility.
- Given this, the Shapes package looks like:
-
- package Shapes is
- type shape is tagged ... ;
- procedure draw (x : shape) is <>; -- abstract
- end Shapes;
-
- This is Alex Blakemore's original definition. (Is this name right?)
-
- Now, draw is an abstract operation of the type shape. However,
- since it is abstract, it must be redefined for each child type of
- shape. An example is:
-
- with Shapes;
- package Circles is
- type circle is new Shapes.shape ...;
- -- inherits primitive ops of shape
- procedure draw (x : circle);
- -- overide draw, now circle is not abstract
- end Circle;
- This is also Alex Blakemore's definition I believe. After this
- definition, draw is a primitive operation of circle.
-
- In order to get dispatching, we need to have an operand which is of
- a class-wide type. Alex's later example shows this.
-
- with Shapes;
- procedure process(x : Shapes.shape'class; y : Shapes.shape);
- begin
- ...
- Shapes.draw(x); -- <-- This is a dispatching call
- Shapes.draw(y); -- <-- This is not a dispatching call, since
- -- the type of y is not class-wide.
- -- In fact, since Shapes.draw is abstract,
- -- this call is not allowed.
- ...
- end process;
-
- The reason I have gone on at such length is that there is another wrinkle
- here. Inside of the body of the circular draw, the compiler assumes that
- the parameter x is actually a circle. This is true even if the operation
- was called through dispatching. For example, imagine that we have:
-
- with Shapes; use Shapes;
- with Circles; use Circles;
- package ProcessGeometry is
- end Process Geometry;
- package body ProcessGeometry is
- circ_object : circle;
- ...
- begin
- draw(Shapes.shape'class(circ_object)); -- dispatching
- draw(circ_object); -- not dispatching
- end ProcessGeometry;
-
- These two calls are to the same procedure, but the first is dispatching
- and the second is not. Inside of the body of circle's draw operation,
- the object x is assumed to be a circle -- this is even if it is in a class
- derived from circle.
-
- We need a better example. This one is somewhat contrived, and is probably
- more long-winded than necessary, but it illustrates the main point.
- Imagine first that I have a Geometry manager package which has an operation
- to draw arbitrary circular arcs. This operation is given by:
- package GeometryManager is
- type Point is ...;
- type Distance is ...;
- type Angle is ...;
- DrawArc(Center : Point; Radius : Distance;
- StartAngle : Angle; EndAngle: Angle);
- end GeometryManager;
-
-
- Imagine furthermore that I have several possible ways of representing circles.
- One is as a center and a radius. Another is as three points which lie
- on the circle. There may be more. These representations will be child
- types of circle.
-
- Now, we flesh out the implementation of circles somewhat. The draw
- routine of a circle will acquire the center and radius of the
- circle and then call the GeometryManager's routine to draw a circular
- arc. The two representations of circles have different ways of
- getting the center and radius. In one they are there to look up,
- and in the other they must be calculated using a standard formula.
- In any case, the operations "calculate the center" and "calculate
- the radius" are different operations for the different child types
- of circle. We make them dispatching operations.
- with Shapes;
- with GeometryManager;
- package Circles is
- type circle is new Shapes.shape ...;
- procedure draw(x : circle);
- function center(x : circle) return GeometryManager.Point is <>;
- function radius(x : circle) return GeometryManager.Distance is <>;
- end Circles;
-
- package body Circles is
- procedure draw(x : circle) is
- begin
- --
- -- If we do some calculation using the center of x,
- -- we will get the non-dispatching operation here.
- -- For example, since the circular center is abstract,
- -- ..., center(x), ...
- -- would not be allowed.
- --
- GeometryManager.DrawArc(center(circle'class(x)),
- -- ^ Notice the conversion
- radius(circle'class(x)),
- -- ^ Notice the conversion
- Angle(0), Angle(360));
- end draw;
- end Circles;
-
- We want the operations center and radius to be dispatching. We must
- convert the parameter x to be of class wide type inside of the body of
- draw. Even though the compiler things that the type of x is circle,
- the run-time type of x is some child type of circle. This is the
- only way to get re-dispatching behavior.
-
- The MRT has discussed this at length in the past. Tucker believes that
- it is important to have a syntactic marker to tell when a call is
- dispatching. An alternative rule would be the C++ rule, where all
- dispatching operations dispatch always. (Ada9X dispatching operations
- are roughly equivalent to C++ virtual operations.) This means that the
- compiler could not replace a dispatching call with a non-dispatching
- call even if the compiler knows where the routine is going to go
- anyway. This is considered expensive.
-
- Peace,
- Bill White
-