Child Packages & Hierarchical Libraries

Build hierarchical libraries with public and private child packages, understand child visibility of the parent's private part, private with, separate subunits, and how the standard library (Ada.Text_IO, Ada.Containers.Vectors) uses hierarchy.

Child packages let you extend a package without modifying it or reopening its source. A child such as Shapes.Text has access to its parent's private declarations, so it can implement extra operations efficiently, while clients that don't need the extension never depend on it. Ada's own standard library is organized this way: Ada, Ada.Strings, Ada.Strings.Unbounded, Ada.Strings.Unbounded.Text_IO.

Public Child Packages

'package Shapes.Text is ... end Shapes.Text;' declares a child of Shapes. A child implicitly 'withs' its parent: inside it, Rect and Area are directly visible. The child's private part and body can see the parent's private part — Shapes.Scaling builds a Rect aggregate from its hidden components W and H. The child's visible part cannot (otherwise clients would see private details through it). Clients write 'with Shapes.Text;' (which also makes Shapes available) and call Shapes.Text.Image.

• package Parent.Child — extends Parent without editing it. • Child body/private part see Parent's private part. • with Parent.Child implies with Parent. • Group optional features into children; clients pay only for what they with.

Private Children, private with, Subunits

'private package Shapes.Internal is ...' declares a private child: only units within the Shapes hierarchy may with it (and public siblings may with it only from their bodies or with 'private with'), so it is ideal for implementation helpers. 'private with Some_Unit;' makes a unit visible only in the private part and body of a spec. A subunit ('separate (Parent)') lets you move a large subprogram or package body out of its enclosing body into its own file: 'procedure Big is separate;' in the body and 'separate (Pkg) procedure Big is ... end Big;' in its file.

• private package P.Internal — usable only within the P hierarchy. • private with X; — X visible only in the private part and bodies. • is separate; + separate (Parent) ... — split big bodies into files.

Code Examples

Shapes.Text and Shapes.Scaling extend Shapes; their bodies read and build Rect components that clients cannot see. Shapes.Internal is a private helper child used by Shapes.Text's body. Output: R = 3 x 4 (area 12) R * 10 = 30 x 40 (area 1200)

A unit outside the hierarchy trying to use the private child. Real GNAT diagnostics: l28_err.adb:1:06: error: unit in with clause is private child unit l28_err.adb:1:06: error: current unit must also have parent "Shapes" l28_err.adb:1:12: warning: unit "Shapes.Internal" is not referenced [-gnatwu] -- editing shapes.ads every time a new feature is needed package Shapes.New_Feature is... end Shapes.New_Feature;

Extend with children to keep the parent stable and avoid recompiling all its clients.

package Shapes.Text is function Width (R: Rect) return Natural is (R.W); -- in the visible part -- declare function Width (R: Rect) return Natural; in the visible part -- and implement it in the child's body, where R.W is visible

The child's visible part cannot see the parent's private part; use the body (or the child's private part).

with Shapes.Internal; -- from an unrelated unit -- only units within Shapes.* may use a private child

Private children are implementation details of their hierarchy.

Can the body of Shapes.Text see the private part of Shapes? A. Yes B. No C. Only with use D. Only if Shapes is Pure

Who may 'with' a private child package P.Internal? A. Anyone B. Only units in the P hierarchy (siblings' bodies, etc.) C. Only the main program D. Nobody

What does 'is separate' do? A. Makes a subprogram abstract B. Moves a body into a separate subunit file C. Starts a task D. Creates a child package

What does 'with Ada.Strings.Unbounded.Text_IO;' imply? A. Nothing else B. Its ancestors Ada, Ada.Strings, Ada.Strings.Unbounded are also withed C. A use clause D. A private with Why is this rejected?

Real GNAT output: l28_err.adb:1:06: error: unit in with clause is private child unit l28_err.adb:1:06: error: current unit must also have parent "Shapes" l28_err.adb:1:12: warning: unit "Shapes.Internal" is not referenced [-gnatwu]

-- file: shapes-internal.ads
--  A private child: only usable inside the Shapes hierarchy
private package Shapes.Internal is
   function Trim (N : Natural) return String;
end Shapes.Internal;

-- file: shapes-scaling.ads
package Shapes.Scaling is
   function Scale (R : Rect; Factor : Positive) return Rect;
end Shapes.Scaling;

-- file: shapes-text.ads
--  A public child package: extends Shapes without modifying it
package Shapes.Text is
   function Image (R : Rect) return String;
end Shapes.Text;

-- file: shapes.ads
package Shapes is
   type Rect is private;
   function Make (W, H : Natural) return Rect;
   function Area (R : Rect) return Natural;
private
   type Rect is record
      W, H : Natural;
   end record;
end Shapes;

-- file: shapes-internal.adb
package body Shapes.Internal is
   function Trim (N : Natural) return String is
      S : constant String := Natural'Image (N);
   begin
      return S (S'First + 1 .. S'Last);
   end Trim;
end Shapes.Internal;

-- file: shapes-scaling.adb
package body Shapes.Scaling is
   function Scale (R : Rect; Factor : Positive) return Rect is
     ((W => R.W * Factor, H => R.H * Factor));   --  private components visible
end Shapes.Scaling;
…
-- file: shapes-internal.ads
--  A private child: only usable inside the Shapes hierarchy
private package Shapes.Internal is
   function Trim (N : Natural) return String;
end Shapes.Internal;

-- file: shapes.ads
package Shapes is
   type Rect is private;
   function Make (W, H : Natural) return Rect;
   function Area (R : Rect) return Natural;
private
   type Rect is record
      W, H : Natural;
   end record;
end Shapes;

-- file: shapes-internal.adb
package body Shapes.Internal is
   function Trim (N : Natural) return String is
      S : constant String := Natural'Image (N);
   begin
      return S (S'First + 1 .. S'Last);
   end Trim;
end Shapes.Internal;

-- file: shapes.adb
package body Shapes is
   function Make (W, H : Natural) return Rect is ((W, H));
   function Area (R : Rect) return Natural is (R.W * R.H);
end Shapes;

-- file: l28_err.adb
with Shapes.Internal;   --  a client outside the hierarchy

procedure L28_Err is
begin
   null;
end L28_Err;