comp.lang.ada
 help / color / mirror / Atom feed
* Re: Uncle
  1998-05-17  0:00 Uncle Chip Richards
@ 1998-05-17  0:00 ` Brian Rogoff
  1998-05-24  0:00   ` Uncle Matthew Heaney
  1998-05-24  0:00 ` Uncle Matthew Heaney
  1 sibling, 1 reply; 5+ messages in thread
From: Brian Rogoff @ 1998-05-17  0:00 UTC (permalink / raw)



On 17 May 1998, Chip Richards wrote:
> On to specific questions.  First, there's an Object type (a tagged record--an
> *abstract private* tagged record at the moment, because that seemed like the
> right thing to do), from which about half the various widget types are
> derived, and a Composite type (called "puInterface" in the original code)
> which is derived from Object, and from which the rest of the widgets are
> derived.  Composite adds a pointer to a list of child Object items, and each
> Object contains a pointer to its parent Composite.  Since the two types are
> interdependent in this way, they must be declared in the same package spec,
> right?  No other choice?  So my urge to make Composites a child package off
> Objects is misguided?

One way out of this bind is to create dummy parent tagged types for the 
tagged types involved in the mutual dependency, and then defining your 
original classes in terms of those dummy types. In your case, 

package Object_Refs is 
    type Object_Parent_Type is abstract tagged null record;
    type Object_Ref_Type is access all Object_Parent_Type'Class;
end Object_Refs;

package Composite_Refs is
    type Composite_Parent_Type is abstract tagged null record;
    type Composite_Ref_Type is access all Composite_Parent_Type'Class;
end Composite_Refs;

package Composites
    type Composite_Type is new Composite_Parent_Type with private;
    function Add_To_List(C : Composite_Type; O_Ref : Object_Ref_Type);
end Composites;

package Objects
    type Object_Type is new Object_Parent_Type with private;
    function Get_Container(O : Object_Type) return Composite_Ref_Type;
end Composites;

You might also want to take a look at John Volan's web page 

http://www.bluemarble.net/~jvolan/WithingProblem/FAQ.html

on the topic of handling mutual dependencies in Ada. 

-- Brian






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

* Uncle
@ 1998-05-17  0:00 Chip Richards
  1998-05-17  0:00 ` Uncle Brian Rogoff
  1998-05-24  0:00 ` Uncle Matthew Heaney
  0 siblings, 2 replies; 5+ messages in thread
From: Chip Richards @ 1998-05-17  0:00 UTC (permalink / raw)



I undertook this project with complete confidence that it was fairly
straightforward, and well within my capabilities.  Now, I am having doubts.
I'm converting a small body of C++ code to Ada.  I've done precious little C++
coding, yet I feel as if I understand pretty much completely what the original
code is doing.  But when I try to express those concepts in Ada, I find myself
tangled in a thorn bush of primitive operations, abstract types, and circular
elaboration dependencies.  I continue to believe that this project isn't
inherently harder to do in Ada than in C++, but at this point in my life,
apparently it's harder for *me*.  And it's not the code per se that is
troubling me, it's how to *structure* it--how (and perhaps whether) to break
it up into separate packages.  If I were willing to make it all one huge
package, I'd be done by now.

For reference, this is a small GUI toolkit named PUI.  Both the original C++
code and my current mangled efforts are on the web, the former at
"http://web2.airmail.net/sjbaker1/pui/" and the latter at
"http://www.niestu.com/languages/oglada/apui/apui-dev.html".  If anyone is
interested enough in these problems to want to see simpler, abstracted
examples, I'd be happy to provide them.  There just aren't any Ada people
around me that I can discuss this with, so I'm turning to the newsgroup.  (And
thanks to David Hoos, who got me out of an earlier rut.  It's not his fault
that I'm now in a much larger ditch. <grin>)  And in case anyone is wondering,
the problems arise entirely from me, and not from the original code--Steve
Baker, author of PUI, is a master with C++.  His code is as clean as anyone
could want.

On to specific questions.  First, there's an Object type (a tagged record--an
*abstract private* tagged record at the moment, because that seemed like the
right thing to do), from which about half the various widget types are
derived, and a Composite type (called "puInterface" in the original code)
which is derived from Object, and from which the rest of the widgets are
derived.  Composite adds a pointer to a list of child Object items, and each
Object contains a pointer to its parent Composite.  Since the two types are
interdependent in this way, they must be declared in the same package spec,
right?  No other choice?  So my urge to make Composites a child package off
Objects is misguided?

Right now, Object and Composite are defined in the topmost package, called
"PUI", along with their primitive operations.  There are child packages
PUI.Objects and PUI.Composites, which define various related procedures, but
only "internal" ones--all the user-visible subprograms that operate directly
on those two types are also in package PUI.  I already don't like that
structure, but I've got more pressing problems at the moment.

One suggestion I've received is to combine Object and Composite into a single
type.  I could do that, but it seems as if it would lose some information--all
Composites are Objects, but not all Objects are Composites.

In fact, my overall tendency is to want to put each of the various types and
their associated subprograms together in separate packages.  And because most
of the "major" user-visible types are derived from Object (which is itself not
used at the "user" level), I tend to think in terms of defining Object in a
"parent" package, and making all the other packages children of it.  However,
I'm starting to get the idea that this isn't a very good plan.  Every time I
try to split the code into neat chunks, a powerful force seems to want me to
glom them into one giant package.  Ugh.

Many of my problems doubtless stem from my practice of patterning the Ada
structure after the C++ structure.  I mean, the C++ is broken up into (fairly)
tidy separate little source files, which seemed like a nice structure to use.
This is starting to seem like more and more of a mistake, and my current
temptation is to rip the code apart and bolt it back together some other way.

But, which way?  Should I create independent packages like, say, Objects (as
opposed to the current PUI.Objects) and Boxes, and include *those* in a main
"PUI" package?  The problem with that is, for user programs to see primitive
ops of type Object, they would have to "with" package Objects.  Now, to my way
of thinking, that's just silly--users don't use "Objects", they use "Buttons"
and "Sliders" and so forth.  Including a seemingly useless package because the
language makes you do it that way seems wrong to me.

I'm pretty sure it's me and not the language, however, and I'm sure some of
you out there can do this stuff in your sleep.  I'm open to any suggestions.
In case it matters, I'm using GNAT 3.10p under Linux.  If you've gotten this
far, thanks very much for your time!

-- 
Chip




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

* Re: Uncle
  1998-05-17  0:00 Uncle Chip Richards
  1998-05-17  0:00 ` Uncle Brian Rogoff
@ 1998-05-24  0:00 ` Matthew Heaney
  1 sibling, 0 replies; 5+ messages in thread
From: Matthew Heaney @ 1998-05-24  0:00 UTC (permalink / raw)



In article <6jlk5s$mob@tomquartz.niestu.com>, chipr@niestu.com (Chip
Richards) wrote:


>On to specific questions.  First, there's an Object type (a tagged record--an
>*abstract private* tagged record at the moment, because that seemed like the
>right thing to do), from which about half the various widget types are
>derived, and a Composite type (called "puInterface" in the original code)
>which is derived from Object, and from which the rest of the widgets are
>derived.  Composite adds a pointer to a list of child Object items, and each
>Object contains a pointer to its parent Composite.  Since the two types are
>interdependent in this way, they must be declared in the same package spec,
>right?  No other choice?  So my urge to make Composites a child package off
>Objects is misguided?

This idiom is straight out of the GofF book (see the Composite pattern).

As always, the answer is, "It depends."

What is a composite doing to its children?  It sounds like they need to go
in the same package, but, you could just declare the root of the object
hierarchy and just the primitive operations needed by composite, in package
visitible to the composite package.  For example,

package PUI Is

   type Root_Object is abstract tagged ...;
   type Object_Access is access all Root_Object'Class;

   <primitive operations needed by composite>

   type Object_List is ...;  <collection of Object_Access items>

   type Root_Composite is 
      abstract tagged record
         List : Object_List;
      end record;

end PUI;


>Right now, Object and Composite are defined in the topmost package, called
>"PUI", along with their primitive operations.  There are child packages
>PUI.Objects and PUI.Composites, which define various related procedures, but
>only "internal" ones--all the user-visible subprograms that operate directly
>on those two types are also in package PUI.  I already don't like that
>structure, but I've got more pressing problems at the moment.

Why don't you like that structure?  

I think you could just isolate the primitive operations just required by
composite in the scope visible to the composite type.  That's what I did
above.  You can still have child packages, as in

package PUI.Objects is

   type Object is new Root_Object with ...;

   <the rest of the primitive ops for Object>

end;

package PUI.Composites is

   -- 
   -- I don't know if you really need this package.
   --

   type Composite is new Root_Compositive with ...;

   <the rest of the primitive ops for Composite>

end;

What you could do is move the declaration of root object into the private
region of PUI; then those "special operations" needed only by composite
could be declared there, so only children (like Composites) would have
visibility.  The "real" object type is declared its own package, as above. 
Something like

package PUI is
...
private

   type Root_Object is abstract tagged ...;
   type Object_Access is access all Root_Object'Class;

   <primitive ops for Root_Object needed by Composite>

end PUI;

with PUI.Composites;
package PUI.Objects is

   type Object is tagged private;

   <the public ops of object>

private

   type Object is
      new Root_Object with record
         Parent : Composite_Access;
 ...
      end record;
        

end PUI.Objects;

package PUI.Composite is

   type Composite is ...;
   type Composite_Access is access all Composite'Class;

   <primitive ops of Composite>

private

   type Object_List is ...;

   type Composite is
      tagged record
         List : Object_List;
 ....
      end record;

end;

Now Composite has visibility to (dispatching) operations of Object, which
it can use to implement its own primitive operations.

>One suggestion I've received is to combine Object and Composite into a single
>type.  I could do that, but it seems as if it would lose some information--all
>Composites are Objects, but not all Objects are Composites.

No, I don't think this is required.  Because you're manipulating pointers
to things, I don't think you have any real problem with mutual spec
dependencies.  Composite and Object can be declared in separate packages.

Actually, you probably want to declare the Root_Object in the public part
of PUI, because you probably need to declare it in the public part of
Composite, as in

package PUI.Composite is

   type Composite is ...;

   procedure Add_Child
      (C : in out Composite;  Child : Root_Object'Class);
or maybe
   procedure Add_Child
      (C : in out Composite; Child : Object_Access);
...
end PUI.Composite;

 

>In fact, my overall tendency is to want to put each of the various types and
>their associated subprograms together in separate packages.  And because most
>of the "major" user-visible types are derived from Object (which is itself not
>used at the "user" level), I tend to think in terms of defining Object in a
>"parent" package, and making all the other packages children of it.  However,
>I'm starting to get the idea that this isn't a very good plan.  Every time I
>try to split the code into neat chunks, a powerful force seems to want me to
>glom them into one giant package.  Ugh.

I think you can put them in separate child packages, though it may require
a downcast in the body of composite (from Root_Object to just Object) or
maybe the other way around (downcase from Root_Composite to Composite in
the body of Objects).

In any case, I think you need one root type visible to the other.  Either this

package PUI is

   type Root_Composite is ...;

end;

package PUI.Objects is

   type Object is ...;

   function New_Object (Parent : Composite_Access) return Object'Class;
   -- (ops only depend on Root_Composite, not PUI.Composites.Composite)

end;

with PUI.Objects;
package PUI.Composites is

   type Composite is new Root_Composite ...;

   <ops that depend on Object>

end;


-or- 

package PUI is

   type Root_Object is ...

 end;

with PUI.Composites;
package PUI.Objects is

   type Object is new Root_Object ...;

   <ops that depend on Composite type>

end;

package PUI.Composites is

   type Composite is ...;

   procedure Add_Child (C : in out Composite; Child : in Object_Access);
   
   -- (ops only depend of Root_Object, not on PUI.Objects.Object)

end;


Without studying your application some more, I can't say which approach is
better.  But the general concept is that a root type (acting as a sort of
type hook, to grab on to) must be available to the other.   (Perhaps this
root type can be declared in the private part of PUI, but I don't think so,
because it's used as an argument to the other type's public, primitive
operations.)

>Many of my problems doubtless stem from my practice of patterning the Ada
>structure after the C++ structure.  I mean, the C++ is broken up into (fairly)
>tidy separate little source files, which seemed like a nice structure to use.
>This is starting to seem like more and more of a mistake, and my current
>temptation is to rip the code apart and bolt it back together some other way.

The language has a profound influence on the design of the application.  It
wouldn't surprise me one ounce that the C++ structure isn't appropriate for
Ada.  But don't give up yet, as  I think it's possible to more or less
follow the C++ structure.

>But, which way?  Should I create independent packages like, say, Objects (as
>opposed to the current PUI.Objects) and Boxes, and include *those* in a main
>"PUI" package?  The problem with that is, for user programs to see primitive
>ops of type Object, they would have to "with" package Objects.  Now, to my way
>of thinking, that's just silly--users don't use "Objects", they use "Buttons"
>and "Sliders" and so forth.  Including a seemingly useless package because the
>language makes you do it that way seems wrong to me.

I think it's best to have a package that acts as the root of the subsystem,
and you are correct in creating a PUI package.  All other packages should
be children of PUI.

>I'm pretty sure it's me and not the language, however, and I'm sure some of
>you out there can do this stuff in your sleep.  I'm open to any suggestions.
>In case it matters, I'm using GNAT 3.10p under Linux.  If you've gotten this
>far, thanks very much for your time!

Feel free to email any time.  You should have other Ada users to talk to,
and I am willing to help.

(I'm trying to boostrap GNAT 3.10p under LinuxPPC.  Maybe you can help me
too...)

Matt




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

* Re: Uncle
  1998-05-17  0:00 ` Uncle Brian Rogoff
@ 1998-05-24  0:00   ` Matthew Heaney
  1998-05-25  0:00     ` Uncle Brian Rogoff
  0 siblings, 1 reply; 5+ messages in thread
From: Matthew Heaney @ 1998-05-24  0:00 UTC (permalink / raw)



In article <Pine.BSF.3.96.980517153518.12746A-100000@shell5.ba.best.com>,
Brian Rogoff <bpr@shell5.ba.best.com> wrote:

>You might also want to take a look at John Volan's web page 
>
>http://www.bluemarble.net/~jvolan/WithingProblem/FAQ.html
>
>on the topic of handling mutual dependencies in Ada. 

But also read my recent response to Adam's query re mutual dependencies.  I
disagree with much of John's advice, and think that there are simpler
solutions.  This particular "problem" with Ada is basically a red herring.

Also, read my discription of the Composite pattern in the Mar 97 Listserv
Patterns Archives of the ACM SIGAda Patterns Working Group:

<http://www.acm.org/sigada/wg/patterns/>

(I have trouble navigating there sometimes; let me know if you have trouble
finding it.)




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

* Re: Uncle
  1998-05-24  0:00   ` Uncle Matthew Heaney
@ 1998-05-25  0:00     ` Brian Rogoff
  0 siblings, 0 replies; 5+ messages in thread
From: Brian Rogoff @ 1998-05-25  0:00 UTC (permalink / raw)



On Sun, 24 May 1998, Matthew Heaney wrote:
> In article <Pine.BSF.3.96.980517153518.12746A-100000@shell5.ba.best.com>,
> Brian Rogoff <bpr@shell5.ba.best.com> wrote:
> 
> >You might also want to take a look at John Volan's web page 
> >
> >http://www.bluemarble.net/~jvolan/WithingProblem/FAQ.html
> >
> >on the topic of handling mutual dependencies in Ada. 
> 
> But also read my recent response to Adam's query re mutual dependencies.  I
> disagree with much of John's advice, and think that there are simpler
> solutions.  This particular "problem" with Ada is basically a red herring.

The solution I described is the same as the one from Norman Cohen's
excellent "Ada as a Second Language". The basic idea is to create dummy 
abstract parent types for those types involved in a mutual dependency.

If you've read Ed Seidewitz's OOPSLA '94 paper "Genericity versus
Inheritance Reconsidered" you'll see how some features of inheritance can
be modeled with genericity.

John Volan's approach is an application of this technique to replace the 
inheritance in Norm Cohen's workaround with genericity, and the built-in 
narrowing and widening to instantiation of generic functions, in this case 
Unchecked_Conversions.

In practice, I've found that the dummy parent approach works fine for me, 
but I find the idea of creating dummy types esthetically unappealing.
I don't have experience with a design that required hundreds or thousands 
of mutually referring types, so I don't know how the workaround scales, or 
if its even necessary to scale it up that much. I have used the Composite
pattern in Java, where it was intuitive, and that can't really be said for 
Ada.

-- Brian
 
> Also, read my discription of the Composite pattern in the Mar 97 Listserv
> Patterns Archives of the ACM SIGAda Patterns Working Group:
> 
> <http://www.acm.org/sigada/wg/patterns/>
> 
> (I have trouble navigating there sometimes; let me know if you have trouble
> finding it.)

> 





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

end of thread, other threads:[~1998-05-25  0:00 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-05-17  0:00 Uncle Chip Richards
1998-05-17  0:00 ` Uncle Brian Rogoff
1998-05-24  0:00   ` Uncle Matthew Heaney
1998-05-25  0:00     ` Uncle Brian Rogoff
1998-05-24  0:00 ` Uncle Matthew Heaney

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