From: Gene on
First, thanks for past help from this group.

I'm at an impasse developing a graph data structure. There are many
node types derived from a "most general" one. Most of the node types
contain fields that are classwide access to child nodes. Various
primitive procedures operate on nodes, dispatching on unnamed node
access types. In many cases, the operations return such an access
child value. Other "identity" ops just return the dispatching
parameter as classwide access.

Here is the idea:

----- hoo.ads ----
package Hoo is

type Node_Type is tagged record
I : Integer := 0;
end record;
function Op(P : access Node_Type) return access Node_Type'Class;

type Threat_Type is new Node_Type with record
Target : access Node_Type'Class;
end record;
overriding
function Op(P : access Threat_Type) return access Node_Type'Class;

end Hoo;

---- hoo.adb ----
package body Hoo is

function Op(P : access Node_Type) return access Node_Type'Class is
begin
return P; -- identity operation
end Op;

overriding
function Op(P : access Threat_Type) return access Node_Type'Class
is
begin
return P.Target; -- just return a child
end Op;

end Hoo;

---- foo.adb (main procedure) ----
with Hoo; use Hoo;

procedure Foo is
Threat : access Threat_Type;
begin
-- Nonsense just to verify type compatibility.
Threat := new Threat_Type;
Threat.Target := Op(Threat);
end Foo;
-----

This is all fine. The trouble is that some of the primitive ops need
to return multiple values, some of which will be classwide access to
node. The "natural" implementation seems to be a procedure with
several "out" parameters; but, "seem" is the operative word. To use
out parameters, the classwide access node type must be named. The
problem here is I can't see a way to dispatch on this named access
type, which will be necessary to recursively operate on child nodes.

One idea is to return a record type that holds the needed multiple
values, but this seems ugly. It will require defining a bunch of
ephemeral record types that don't have clear meaning as objects.

I tentatively have given up on access dispatching entirely,
dispatching instead on node types (not access) and copying to form
fresh values of a named classwide access type for the return
value(s). This same named type is used for node child pointers.
While this okay, there is a lot of useless copying.

Feels like I'm playing whack-a-mole with the Ada type system. What's
the idomatic way to get this job done without the copying?

Thanks,
Gene
From: Ludovic Brenta on
Gene writes:
> I tentatively have given up on access dispatching entirely,
> dispatching instead on node types (not access) and copying to form
> fresh values of a named classwide access type for the return
> value(s). This same named type is used for node child pointers.
> While this okay, there is a lot of useless copying.
>
> Feels like I'm playing whack-a-mole with the Ada type system. What's
> the idomatic way to get this job done without the copying?

Parameters of tagged types being always passed by reference (see ARM
6.3.2(5)), there is no copying involved in "dispatching on node types"
as opposed to access, so I think your solution is fine. Then again, I
may have misunderstood your problem.

--
Ludovic Brenta.
From: Gene on
On Jan 20, 8:01 pm, Ludovic Brenta <ludo...(a)ludovic-brenta.org> wrote:
> Gene writes:
> > I tentatively have given up on access dispatching entirely,
> > dispatching instead on node types (not access) and copying to form
> > fresh values of a named classwide access type for the return
> > value(s).  This same named type is used for node child pointers.
> > While this okay, there is a lot of useless copying.
>
> > Feels like I'm playing whack-a-mole with the Ada type system.  What's
> > the idomatic way to get this job done without the copying?
>
> Parameters of tagged types being always passed by reference (see ARM
> 6.3.2(5)), there is no copying involved in "dispatching on node types"
> as opposed to access, so I think your solution is fine.  Then again, I
> may have misunderstood your problem.
>

I haven't been clear. When I dispatch on the tagged record types
rather than access to tagged record, then I have no way of returning a
pointer to the dispatching object or a child pointer.

In concept, what I'd need to do is something like this:

type Node_Ptr_Type is access Node_Type'Class;

procedure Op(P : in Threat_Type; Result : out Node_Ptr_Type) is
begin
Result := P'Access; -- identity operation
end Op;

... or the other way:

procedure Op(P : access Threat_Type; Result : out Node_Ptr_Type) is
begin
Result := Node_Ptr_Type(P); -- identity operation
end Op;

Of course neither of these is correct Ada. Instead I'm doing this:

procedure Op(P : in Threat_Type; Result : out Node_Ptr_Type) is
begin
Result := Node_Ptr_Type'(new Threat_Type'(P)); -- identity
operation
end Op;

This works, but the copy is unnecessary.