* Proper program structure @ 2009-09-30 21:25 Maciej Sobczak 2009-09-30 22:16 ` Robert A Duff ` (3 more replies) 0 siblings, 4 replies; 13+ messages in thread From: Maciej Sobczak @ 2009-09-30 21:25 UTC (permalink / raw) First, thank you all for the replies to my previous questions. But I have more. :-) 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. 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 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 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? -- Maciej Sobczak * www.msobczak.com * www.inspirel.com Database Access Library for Ada: www.inspirel.com/soci-ada ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-09-30 21:25 Proper program structure Maciej Sobczak @ 2009-09-30 22:16 ` Robert A Duff 2009-10-01 7:13 ` Maciej Sobczak 2009-09-30 23:43 ` Adam Beneschan ` (2 subsequent siblings) 3 siblings, 1 reply; 13+ messages in thread From: Robert A Duff @ 2009-09-30 22:16 UTC (permalink / raw) Maciej Sobczak <see.my.homepage@gmail.com> writes: > 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? I think your design problem can probably be solved by "limited with". - Bob ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-09-30 22:16 ` Robert A Duff @ 2009-10-01 7:13 ` Maciej Sobczak 0 siblings, 0 replies; 13+ messages in thread From: Maciej Sobczak @ 2009-10-01 7:13 UTC (permalink / raw) On 1 Paź, 00:16, Robert A Duff <bobd...@shell01.TheWorld.com> wrote: > I think your design problem can probably be solved by "limited with". Which package should be "limited withed" by which one? If you mean that Cars should "limited with" its components, then we are at 4b. anyway, which means tha components should be allocated dynamically ("Java in Ada"). -- Maciej Sobczak * www.msobczak.com * www.inspirel.com Database Access Library for Ada: www.inspirel.com/soci-ada ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-09-30 21:25 Proper program structure Maciej Sobczak 2009-09-30 22:16 ` Robert A Duff @ 2009-09-30 23:43 ` Adam Beneschan 2009-10-01 7:35 ` Maciej Sobczak 2009-10-01 6:34 ` Brad Moore 2009-10-01 8:08 ` Dmitry A. Kazakov 3 siblings, 1 reply; 13+ messages in thread From: Adam Beneschan @ 2009-09-30 23:43 UTC (permalink / raw) On Sep 30, 2:25 pm, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > 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 > > 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 Just for the record, I've had occasion in the past to believe that this should be allowed: type Car is limited record Gear_Box : aliased Gear_Box_Type; Engine : Engine_Type (Car.Gear_Box'Access); ... The syntax is slightly different from what you're looking for, but it's more consistent with the current Ada rules since I think there's nowhere else in the language (besides record rep clauses) where a record component can be reference without some sort of prefix selector, but there's already a facility for self-references to types. The only reason this is illegal is because of 3.8(13), which limits the use of a type self-reference in a constraint to T'Access or T'Unchecked_Access, which must appear by itself. This limitation is partly because allowing certain other uses of type self-references would cause problems, and partly to avoid some unnecessary complexities in compilers if arbitrary expressions were allowed. But I think this could be relaxed a bit to allow T.component [.component...]'Access, with some restrictions. As long as this were restricted so that the 'Access value would always be a constant offset from the beginning of the record in a typical compiler, I don't think this would add much complexity, and it would solve some problems, such as yours (I presume---I haven't studied it too closely), and a program I've seen earlier which would have been more straightforward if this feature were allowed but instead had to resort to something a bit tricky. Anyway, maybe this will motivate me to take a second look at that earlier program and perhaps submit a proposal for a new language feature. Offhand, I think the attribute may have to be 'Unchecked_Access to get around accessibility level rules, but I don't recall. -- Adam ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-09-30 23:43 ` Adam Beneschan @ 2009-10-01 7:35 ` Maciej Sobczak 0 siblings, 0 replies; 13+ messages in thread From: Maciej Sobczak @ 2009-10-01 7:35 UTC (permalink / raw) On 1 Paź, 01:43, Adam Beneschan <a...@irvine.com> wrote: > Just for the record, I've had occasion in the past to believe that > this should be allowed: > > type Car is limited record > Gear_Box : aliased Gear_Box_Type; > Engine : Engine_Type (Car.Gear_Box'Access); > ... Yes, that was one of my ideas. > Anyway, maybe this will motivate me to take a second look at > that earlier program and perhaps submit a proposal for a new language > feature. If we are at potential language proposals, then my problem would be also solved with some conceptual combination of limited with and private child packages. Imagine that Car's components are defined in their private child packages (Car.Engine, Car.Gear_Box, etc.). These packages are withed by Car. Now, to avoid the cyclic dependency between package and its children, I would need to somehow express that the specification of the child package does not need to see the parent anymore than on the "limited" basis, whereas the body of the child package would still have a choice to see the parent spec completely. To be more exact: with Cars.Engine; -- not possible today with Cars.Gear_Box; package Cars is ... private type Car is record Engine : Engine_Type (Car'Access); Gear_Box : Gear_Box_Type (Car'Access); end record; end Cars; -- now the new magic child spec limited private package Cars.Engine is type Engine_Type (C : access Car) is ... end Cars.Engine; -- and the new magic child body with Cars; -- necessary for a complete view of Cars with Cars.Gear_Box; package body Cars.Engine is procedure Run (E : in out Engine_Type) is begin E.C.all.Gear_Box.Move_This_As_Well; end Run; end Cars.Engine; Note: 1. parent package Cars withs its child package Engine, so that it can use Engine_Type as component of the Car 2. specification of child package Cars.Engine does not see anything more than a limited view on its parent's types, so it can use access to Car 3. body of child package Cars.Engine can see complete parent spec, because it withs Cars explicitly Technically it should be possible to compile everything without cycles. The whole magic comes down to the additional form of childhood, where the child does not fully depend on the parent. In other words, the same trick that is used in Ada2005 to cut cyclic dependencies between peers (limited with) could be also used to cut cyclic dependencies between parents and their children. That would be awesome and would allow me to write Ada in Ada. -- Maciej Sobczak * www.msobczak.com * www.inspirel.com Database Access Library for Ada: www.inspirel.com/soci-ada ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-09-30 21:25 Proper program structure Maciej Sobczak 2009-09-30 22:16 ` Robert A Duff 2009-09-30 23:43 ` Adam Beneschan @ 2009-10-01 6:34 ` Brad Moore 2009-10-01 7:44 ` Maciej Sobczak 2009-10-01 9:39 ` Maciej Sobczak 2009-10-01 8:08 ` Dmitry A. Kazakov 3 siblings, 2 replies; 13+ messages in thread From: Brad Moore @ 2009-10-01 6:34 UTC (permalink / raw) 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; ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-10-01 6:34 ` Brad Moore @ 2009-10-01 7:44 ` Maciej Sobczak 2009-10-01 9:39 ` Maciej Sobczak 1 sibling, 0 replies; 13+ messages in thread From: Maciej Sobczak @ 2009-10-01 7:44 UTC (permalink / raw) On 1 Paź, 08:34, Brad Moore <brad.mo...@shaw.ca> wrote: > 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. Very nice. Yes, I have missed this option. In my system the packages would have a different structure - Cars is not a root anyway, but everything is in the hierarchy rooted in (let's say) Transport. This allows me to have Cars without any artificial child Vehicle package, and all components are in private package Transport.Components. Thank you for this suggestion. -- Maciej Sobczak * www.msobczak.com * www.inspirel.com Database Access Library for Ada: www.inspirel.com/soci-ada ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-10-01 6:34 ` Brad Moore 2009-10-01 7:44 ` Maciej Sobczak @ 2009-10-01 9:39 ` Maciej Sobczak 2009-10-01 15:36 ` Brad Moore 1 sibling, 1 reply; 13+ messages in thread From: Maciej Sobczak @ 2009-10-01 9:39 UTC (permalink / raw) On 1 Paź, 08:34, Brad Moore <brad.mo...@shaw.ca> wrote: > See my example below. I have each component as a private child of Cars. Well, I was too quick to reply. Your example does not do what I need. It allows the components to see the visible spec of the containing car (in your example, Gear_Box calls *public* procedure Shift_Gear in Vehicle), but not the car's structure. In other words, the Engine still cannot see the Gear_Box within the same car. It is of course possible to expose delegating operations, but this pollutes the public interface of the car with operations that are of interest only to its private components. Users should not have access to these operations. -- Maciej Sobczak * www.msobczak.com * www.inspirel.com Database Access Library for Ada: www.inspirel.com/soci-ada ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-10-01 9:39 ` Maciej Sobczak @ 2009-10-01 15:36 ` Brad Moore 2009-10-01 20:01 ` Maciej Sobczak 0 siblings, 1 reply; 13+ messages in thread From: Brad Moore @ 2009-10-01 15:36 UTC (permalink / raw) Maciej Sobczak wrote: > On 1 Paz', 08:34, Brad Moore <brad.mo...@shaw.ca> wrote: > >> See my example below. I have each component as a private child of Cars. > > Well, I was too quick to reply. > Your example does not do what I need. It allows the components to see > the visible spec of the containing car (in your example, Gear_Box > calls *public* procedure Shift_Gear in Vehicle), but not the car's > structure. > In other words, the Engine still cannot see the Gear_Box within the > same car. > > It is of course possible to expose delegating operations, but this > pollutes the public interface of the car with operations that are of > interest only to its private components. Users should not have access > to these operations. > > -- > Maciej Sobczak * www.msobczak.com * www.inspirel.com > > Database Access Library for Ada: www.inspirel.com/soci-ada OK, but I can apply a similar technique to hide the routines from the public interface of the car. In this case, I renamed the Cars.Vehicle package to be Cars.Vehicle_Internal, then created a public Cars.Vehicle package that wraps the internal one, but hides the interfaces that you do not want to expose. Is this version acceptable? Revised Example: ----------------------------------------------------- with Cars.Vehicle; procedure Main is My_Car : Cars.Vehicle.Vehicle_Type := Cars.Vehicle.Construct; begin null; end Main; ----------------------------------------------- package Cars is pragma Pure; end Cars; -------------------------------------------- private with Cars.Vehicle_Internal; package Cars.Vehicle is type Vehicle_Type (<>) is limited private; function Construct return Vehicle_Type; private use Cars.Vehicle_Internal; type Vehicle_Type is record Internals : Vehicle_Internal_Type; end record; end Cars.Vehicle; ----------------------------------------- package body Cars.Vehicle is function Construct return Vehicle_Type is begin return New_Vehicle : Vehicle_Type := Vehicle_Type'(Internals => Vehicle_Internal.Construct) do return; end return; end Construct; end Cars.Vehicle; ------------------------------------------ with Cars.Types; use Cars.Types; private with Cars.Wheels, Cars.Chassis, Cars.Engine, Cars.Gear_Box; private package Cars.Vehicle_Internal is type Vehicle_Internal_Type is limited private; procedure Shift_Gears (Car : in out Vehicle_Internal_Type; Gear : Gear_Type); function Construct return Vehicle_Internal_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_Internal_Type is limited record Wheels : Wheel_Array_Type; Chassis : Chassis_Type; Engine : Engine_Type; Gear_Box : Gear_Box_Type (Vehicle_Internal_Type'Access); end record; end Cars.Vehicle_Internal; -------------------------------------- package body Cars.Vehicle_Internal is function Construct return Vehicle_Internal_Type is begin return New_Vehicle : Vehicle_Internal_Type do Gear_Box.Select_Gear (New_Vehicle.Gear_Box, Park); return; end return; end Construct; procedure Shift_Gears (Car : in out Vehicle_Internal_Type; Gear : Gear_Type) is begin Cars.Engine.Shift_Gears (Motor => Car.Engine, Gear => Gear); end Shift_Gears; end Cars.Vehicle_Internal; ------------------------------------------- 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; -------------------------- with Cars.Types; use Cars.Types; limited with Cars.Vehicle_Internal; private package Cars.Gear_Box is type Gear_Box_Type (Containing_Vehicle : access Cars.Vehicle_Internal.Vehicle_Internal_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_Internal.Vehicle_Internal_Type) is null record; end Cars.Gear_Box; ------------------------------------- with Cars.Vehicle_Internal; package body Cars.Gear_Box is procedure Select_Gear (Gear_Shift : in out Gear_Box_Type; Gear : Gear_Type) is begin Cars.Vehicle_Internal.Shift_Gears (Car => Gear_Shift.Containing_Vehicle.all, Gear => Gear); end Select_Gear; end Cars.Gear_Box; -------------------------- with Cars.Types; use Cars.Types; 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; ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-10-01 15:36 ` Brad Moore @ 2009-10-01 20:01 ` Maciej Sobczak 2009-10-02 5:44 ` Brad Moore 0 siblings, 1 reply; 13+ messages in thread From: Maciej Sobczak @ 2009-10-01 20:01 UTC (permalink / raw) On 1 Paź, 17:36, Brad Moore <brad.mo...@shaw.ca> wrote: > OK, but I can apply a similar technique to hide the routines from the > public interface of the car. In this case, I renamed the Cars.Vehicle > package to be Cars.Vehicle_Internal, then created a public Cars.Vehicle > package that wraps the internal one, but hides the interfaces that you > do not want to expose. > > Is this version acceptable? Well, it is acceptable in the same way as the other options that I have described. In other words, due to the fact that the right way does not work, I have to use some inferior solution that always has a price in the design that does not accurately reflect the original intent. Thank you very much for you complete examples, but I have finally settled on two-phase initialization of components (so each has an Init procedure that is used to wire them together and all Inits are called from the constructor of the whole). This solution allows to keep the reasonable package hierarchy (no artificial packages are needed) and avoids a number of forwarding subprograms. -- Maciej Sobczak * www.msobczak.com * www.inspirel.com Database Access Library for Ada: www.inspirel.com/soci-ada ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-10-01 20:01 ` Maciej Sobczak @ 2009-10-02 5:44 ` Brad Moore 2009-10-02 13:10 ` Brad Moore 0 siblings, 1 reply; 13+ messages in thread From: Brad Moore @ 2009-10-02 5:44 UTC (permalink / raw) Maciej Sobczak wrote: > On 1 Paz', 17:36, Brad Moore <brad.mo...@shaw.ca> wrote: > >> OK, but I can apply a similar technique to hide the routines from the >> public interface of the car. In this case, I renamed the Cars.Vehicle >> package to be Cars.Vehicle_Internal, then created a public Cars.Vehicle >> package that wraps the internal one, but hides the interfaces that you >> do not want to expose. >> >> Is this version acceptable? > > Well, it is acceptable in the same way as the other options that I > have described. In other words, due to the fact that the right way > does not work, I have to use some inferior solution that always has a > price in the design that does not accurately reflect the original > intent. > > Thank you very much for you complete examples, but I have finally > settled on two-phase initialization of components (so each has an Init > procedure that is used to wire them together and all Inits are called > from the constructor of the whole). This solution allows to keep the > reasonable package hierarchy (no artificial packages are needed) and > avoids a number of forwarding subprograms. You're welcome. It was an interesting challenge. Not sure I fully see how you've structured things, but glad you found something that at least works. I do have one more set of refinements to add to my example though. I can get rid of the artificial packages Cars, and Vehicle, by using an incomplete type declaration. That is, I move the declarations in Cars.Vehicle, to Cars, which is where you wanted to declare the type in the first place. However Vehicle_Type is an access type to an incomplete type that is completed in the body. This allows me to delete the Cars.Vehicle package as it is no longer needed. Since Construct now returns a heap allocated object, I make the type, Vehicle_Internal_Type a controlled type in the package body, so that clients of Cars would not be impacted by memory leakage by declaring a Vehicle_Type on the stack. Also, I made the Cars.Vehicle_Type inherit from Cars.Vehicle_Internal.Vehicle_Internal_Type. That way, I eliminate the forwarding programs. (If that is what you meant by forwarding subprograms), since the primitive subprograms are inherited directly. Well not entirely true. The primitive subprograms are available in the body, but if they need to be exported from the spec, then you still need forwarding subprograms, since Vehicle_Type is an access type. Also, other than the Cars package, all the other child packages are private packages, which cannot be withed by client code. If the goal of this exercise is to be able to define a clean interface that is not impacted by the Ada language restrictions, ie, no evidence or artifacts in the spec resulting from issues that have been discussed, then I think this might be a good approach. If we had to jump through a hoop or two to get the implementations to work, at least we've hidden those hurdles. Of course, the approach you've chosen may still be a better approach for you, since the requirements of your real system may be very much different than this toy example. For example, you may not like the fact that I used heap allocation here, or that I still have a Cars.Vehicle_Internal package, though that is a private package at least. Maybe it just needs a better name. It is perhaps weird that the vehicle_type in Cars effectively inherits from a private child package. Usually it is the other way around, the child package type inherits from the root package type, but I think this gives you the effect that the root package type contains all the details hidden in the child package. To show the refined approach though... package Cars is type Vehicle_Type (<>) is limited private; function Construct return Vehicle_Type; private type Car_Type; type Vehicle_Type is access all Car_Type; end Cars; ------------------------------------------ with Cars.Vehicle_Internal; use Cars.Vehicle_Internal; package body Cars is type Car_Type is new Vehicle_Internal_Type; function Construct return Vehicle_Type is begin return new Car_Type' (Car_Type (Vehicle_Internal_Type'(Construct))); end Construct; end Cars; --------------------------------------------- with Cars.Types; use Cars.Types; private with Cars.Wheels, Cars.Chassis, Cars.Engine, Cars.Gear_Box; private with Ada.Finalization; private package Cars.Vehicle_Internal is type Vehicle_Internal_Type is private; procedure Shift_Gears (Car : in out Vehicle_Internal_Type; Gear : Gear_Type); function Construct return Vehicle_Internal_Type; private use Cars.Wheels, Cars.Chassis, Cars.Engine, Cars.Gear_Box; use Ada.Finalization; type Wheel_Array_Type is array (1 .. 4) of Wheel_Type; type Force_Finalization is new Controlled with null record; type Vehicle_Internal_Type is record Needs_Finalization : Force_Finalization; Wheels : Wheel_Array_Type; Chassis : Chassis_Type; Engine : Engine_Type; Gear_Box : Gear_Box_Type (Vehicle_Internal_Type'Access); end record; end Cars.Vehicle_Internal; ---------------------------------------- package body Cars.Vehicle_Internal is function Construct return Vehicle_Internal_Type is begin return New_Vehicle : Vehicle_Internal_Type do Gear_Box.Select_Gear (New_Vehicle.Gear_Box, Park); return; end return; end Construct; procedure Shift_Gears (Car : in out Vehicle_Internal_Type; Gear : Gear_Type) is begin Cars.Engine.Shift_Gears (Motor => Car.Engine, Gear => Gear); end Shift_Gears; end Cars.Vehicle_Internal; ---------------------------------------- -- Also, forgot to include this package last time. private package Cars.Types is type Gear_Type is (Park, Neutral, Forward, Backward); end Cars.Types; Brad ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-10-02 5:44 ` Brad Moore @ 2009-10-02 13:10 ` Brad Moore 0 siblings, 0 replies; 13+ messages in thread From: Brad Moore @ 2009-10-02 13:10 UTC (permalink / raw) Sorry, need to make a correction to my last posting. The automatic finalization would not have worked like I would have wanted, since the vehicle_type is an access type, finalization would have only occurred when the access type went out of scope (at program finalization). To get the effect I was looking for would have required garbage collection. To correct this, I make the Vehicle_Type a controlled record type containing an access to the internals. I got rid of the inheritance, since it did not seem to be providing much of a benefit. Brad i.e. private with Ada.Finalization; package Cars is type Vehicle_Type (<>) is limited private; function Construct return Vehicle_Type; private use Ada.Finalization; type Car_Type; type Car_Access_Type is access Car_Type; type Vehicle_Type is new Limited_Controlled with record Car : Car_Access_Type; end record; overriding procedure Finalize (Vehicle : in out Vehicle_Type); end Cars; --------------------------- with Cars.Vehicle_Internal; use Cars.Vehicle_Internal; with Ada.Unchecked_Deallocation; package body Cars is type Car_Type is record Internals : Vehicle_Internal_Type; end record; procedure Free_Car is new Ada.Unchecked_Deallocation (Object => Car_Type, Name => Car_Access_Type); function Construct return Vehicle_Type is begin return Vehicle : Vehicle_Type do Vehicle.Car := new Car_Type'(Internals => Vehicle_Internal_Type'(Construct)); return; end return; end Construct; overriding procedure Finalize (Vehicle : in out Vehicle_Type) is begin Free_Car (Vehicle.Car); end Finalize; end Cars; ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Proper program structure 2009-09-30 21:25 Proper program structure Maciej Sobczak ` (2 preceding siblings ...) 2009-10-01 6:34 ` Brad Moore @ 2009-10-01 8:08 ` Dmitry A. Kazakov 3 siblings, 0 replies; 13+ messages in thread From: Dmitry A. Kazakov @ 2009-10-01 8:08 UTC (permalink / raw) On Wed, 30 Sep 2009 14:25:57 -0700 (PDT), Maciej Sobczak wrote: > 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 They could with MI type Engine_Interface is limited interface; --- type Gear_Box_Interface is limited interface; --- type Engine_Type ( Gear_Box : not null access Gear_Box_Interface'Class ) is new Engine_Interface with ... --- type Gear_Box_Type is new Engine_Interface with ... --- type Car is new Ada.Finalization.Limited_Controlled and Engine_Interface and Gear_Box_Interface with private; private type Car is new Ada.Finalization.Limited_Controlled and Engine_Interface and Gear_Box_Interface with record Engine : Engine_Type (Car'Access); Gear_Box : Gear_Box_Type; end record; > Am I over-sensitive or did I really hit a design problem? Yep, any design will ultimately collapse into dynamically typed mess on further constraints like the engines of the class E_123'Class require gear boxes from the class G_M25_X'Class. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2009-10-02 13:10 UTC | newest] Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2009-09-30 21:25 Proper program structure Maciej Sobczak 2009-09-30 22:16 ` Robert A Duff 2009-10-01 7:13 ` Maciej Sobczak 2009-09-30 23:43 ` Adam Beneschan 2009-10-01 7:35 ` Maciej Sobczak 2009-10-01 6:34 ` Brad Moore 2009-10-01 7:44 ` Maciej Sobczak 2009-10-01 9:39 ` Maciej Sobczak 2009-10-01 15:36 ` Brad Moore 2009-10-01 20:01 ` Maciej Sobczak 2009-10-02 5:44 ` Brad Moore 2009-10-02 13:10 ` Brad Moore 2009-10-01 8:08 ` Dmitry A. Kazakov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox