From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: a07f3367d7,efde8669839c1c0a X-Google-Attributes: gida07f3367d7,public,usenet X-Google-NewGroupId: yes X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news2.google.com!news2.google.com!npeer02.iad.highwinds-media.com!news.highwinds-media.com!feed-me.highwinds-media.com!post01.iad.highwinds-media.com!newsfe25.iad.POSTED!7564ea0f!not-for-mail From: Brad Moore User-Agent: Thunderbird 2.0.0.23 (X11/20090817) MIME-Version: 1.0 Newsgroups: comp.lang.ada Subject: Re: Proper program structure References: <638d582c-f275-48a9-aa2a-237f2edd123c@c37g2000yqi.googlegroups.com> In-Reply-To: <638d582c-f275-48a9-aa2a-237f2edd123c@c37g2000yqi.googlegroups.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Message-ID: NNTP-Posting-Host: 70.72.159.148 X-Complaints-To: internet.abuse@sjrb.ca X-Trace: newsfe25.iad 1254378864 70.72.159.148 (Thu, 01 Oct 2009 06:34:24 UTC) NNTP-Posting-Date: Thu, 01 Oct 2009 06:34:24 UTC Date: Thu, 01 Oct 2009 00:34:16 -0600 Xref: g2news2.google.com comp.lang.ada:8551 Date: 2009-10-01T00:34:16-06:00 List-Id: Maciej Sobczak wrote: > Consider the following example. It is imaginary, but shows the actual > problem, even though the analogy is not always exact. > > package Cars is > type Car (<>) is limited private; > ... > private > type Car is limited record > Body : ... > Engine : ... > Gear_Box : ... > Wheels : ... > end record; > > Note: in the actual project some of the components need to know about > the others, but the "links" are always acyclic. > > To keep the sizes of all packages at a reasonable volume I decided to > define each type in a separate package. > The following shows the history of failures: > > 1. Some of the components of the car make sense in isolation and can > be reused in other contexts, but some might be tied to the project > with no chance for any reuse. Intuitively I thought they could be > defined in private children of the Cars package (private means that > users of Car will not use them by mistake and children to physically > reflect the hierarchy). This is not possible, because the Cars package > would need to "with" these private children packages and that > introduces cyclic dependencies. Too bad. See my example below. I have each component as a private child of Cars. I get around the issue you describe by having Cars.Vehicle as a sibling of the other components, and by using private with. Cars is just a root package that only contains some minimal type declarations that are shared between the various packages. > > 2. Defining them in non-child packages means that they cannot see the > complete structure of the Car and therefore they cannot be "wired" by > discriminants like here: > > -- wrong: > type Car is limited record > Body : Body_Type (Car'Access); > Engine : Engire_Type (Car'Access); > -- and so on In my example, I use limited withs and private withs to achieve this. I have a Gear_Box with a discriminant for Car.Vehicle_Type'Access. > 3. So they have to be wired individually, but discriminants will still > not work, as sibling components of the same record cannot be used to > constrain each other: > > -- wrong: > type Car is limited record > Gear_Box : aliased Gear_Box_Type; > Engine : Engire_Type (Gear_Box'Access); > -- and so on Not needed, since I have a solution for point 2 above. > > 4. So they have to be wired individually by some initialization > subprogram, presumably the Car's constructor function (because Car has > a constructor function). > There are two ways of doing it: > > 4a. By defining some Init procedure for each component type, which > will establish the links. This means that all components are aliased > and the Car's constructor function needs to call Init for each > component and provide proper links to dependent components. I don't > like the idea of having two-phase initialization and 'Unchecked_Access > all over the shop, so this solution just sucks. > > 4b. By allocating all components dynamically and keeping them by > access values. They can be allocated in the proper order with proper > links to other components provided at the allocation time (there are > no cycles, so there is always some proper order to do this), but then > it does not look like a composition anymore and it is essentially > "Java in Ada". > > In other words, all the solutions I can think of are either illegal or > crappy. > Am I over-sensitive or did I really hit a design problem? > In the Construct I have used an extended return to return the vehicle without any heap allocations. Does the following approach satisfy your needs? Note: If I had used tagged types, I could also have used the new object prefix notation, which might enhance readability as well. Brad ----------------------------------------------------- with Cars.Vehicle; procedure Main is My_Car : Cars.Vehicle.Vehicle_Type := Cars.Vehicle.Construct; begin null; end Main; ----------------------------------------------- package Cars is pragma Pure; type Gear_Type is (Park, Neutral, Forward, Backward); end Cars; -------------------------------------------- private with Cars.Wheels, Cars.Chassis, Cars.Engine, Cars.Gear_Box; package Cars.Vehicle is type Vehicle_Type (<>) is limited private; procedure Shift_Gears (Car : in out Vehicle_Type; Gear : Gear_Type); function Construct return Vehicle_Type; private use Cars.Wheels, Cars.Chassis, Cars.Engine, Cars.Gear_Box; type Wheel_Array_Type is array (1 .. 4) of Wheel_Type; type Vehicle_Type is limited record Wheels : Wheel_Array_Type; Chassis : Chassis_Type; Engine : Engine_Type; Gear_Box : Gear_Box_Type (Vehicle_Type'Access); end record; end Cars.Vehicle; ----------------------------------------- package body Cars.Vehicle is function Construct return Vehicle_Type is begin return New_Vehicle : Vehicle_Type do Gear_Box.Select_Gear (New_Vehicle.Gear_Box, Park); return; end return; end Construct; procedure Shift_Gears (Car : in out Vehicle_Type; Gear : Gear_Type) is begin Cars.Engine.Shift_Gears (Motor => Car.Engine, Gear => Gear); end Shift_Gears; end Cars.Vehicle; ------------------------------------------- private package Cars.Wheels is type Wheel_Type is private; private type Length_In_Inches is new Natural; subtype Wheel_Diameter_Type is Length_In_Inches range 30 .. 80; type Wheel_Type is record Diameter : Wheel_Diameter_Type := 50; end record; end Cars.Wheels; -------------------------- limited with Cars.Vehicle; private package Cars.Gear_Box is type Gear_Box_Type (Containing_Vehicle : access Cars.Vehicle.Vehicle_Type) is private; procedure Select_Gear (Gear_Shift : in out Gear_Box_Type; Gear : Gear_Type); -- Somehow this gets called, presumably by user action. private type Gear_Box_Type (Containing_Vehicle : access Cars.Vehicle.Vehicle_Type) is null record; end Cars.Gear_Box; -------------------------- with Cars.Vehicle; package body Cars.Gear_Box is procedure Select_Gear (Gear_Shift : in out Gear_Box_Type; Gear : Gear_Type) is begin Cars.Vehicle.Shift_Gears (Car => Gear_Shift.Containing_Vehicle.all, Gear => Gear); end Select_Gear; end Cars.Gear_Box; --------------------------- private package Cars.Engine is type Engine_Type is private; procedure Shift_Gears (Motor : in out Engine_Type; Gear : Gear_Type); private type Engine_Type is record Current_Gear : Gear_Type; end record; end Cars.Engine; --------------------- package body Cars.Engine is procedure Shift_Gears (Motor : in out Engine_Type; Gear : Gear_Type) is begin Motor.Current_Gear := Gear; end Shift_Gears; end Cars.Engine; -------------------- private package Cars.Chassis is type Chassis_Type is private; private type Chassis_Type is null record; end Cars.Chassis;