Packages
Package renaming
We've seen in the Introduction to Ada course that we can rename packages.
In the Ada Reference Manual
Grouping packages
A use-case that we haven't mentioned in that course is that we can apply package renaming to group individual packages into a common hierarchy. For example:
package Driver_M1 is end Driver_M1;package Driver_M2 is end Driver_M2;package Drivers with Pure is end Drivers;with Driver_M1; package Drivers.M1 renames Driver_M1;with Driver_M2; package Drivers.M2 renames Driver_M2;
Here, we're renaming the Driver_M1
and Driver_M2
packages as
child packages of the Drivers
package, which is a
pure package.
Important
Note that a package that is renamed as a child package cannot refer to
information from its (non-renamed) parent. In other words,
Driver_M1
(renamed as Drivers.M1
) cannot refer to information
from the Drivers
package. For example:
package Driver_M1 is Counter_2 : Integer := Drivers.Counter; end Driver_M1;package Drivers is Counter : Integer := 0; end Drivers;with Driver_M1; package Drivers.M1 renames Driver_M1;
As expected, compilation fails here because Drivers.Counter
isn't
visible in Driver_M1
, even though the renaming (Drivers.M1
)
creates a virtual hierarchy.
Child of renamed package
Note that we cannot create a child package using a parent package name that was
introduced by a renaming. For example, let's say we want to create a child
package Ext
for the Drivers.M1
package we've seen earlier. We
cannot just declare a Drivers.M1.Ext
package like this:
package Drivers.M1.Ext is
end Drivers.M1.Ext;
because the parent unit cannot be a renaming. The solution is to actually extend the original (non-renamed) package:
package Driver_M1.Ext is end Driver_M1.Ext;-- A package called Drivers.M1.Ext is -- automatically available! with Drivers.M1.Ext; procedure Dummy is begin null; end Dummy;
This works fine because any child package of a package P
is also a child
package of a renamed version of P
. (Therefore, because Ext
is a
child package of Driver_M1
, it is also a child package of the renamed
Drivers.M1
package.)
Backwards-compatibility via renaming
We can also use renaming to ensure backwards-compatibility when changing the package hierarchy. For example, we could adapt the previous source-code by:
converting
Driver_M1
andDriver_M2
to child packages ofDrivers
, andusing package renaming to mimic the original names (
Driver_M1
andDriver_M2
).
This is the adapted code:
package Drivers with Pure is end Drivers;-- We've converted Driver_M1 to -- Drivers.M1: package Drivers.M1 is end Drivers.M1;-- We've converted Driver_M2 to -- Drivers.M2: package Drivers.M2 is end Drivers.M2;-- Original Driver_M1 package still -- available via package renaming: with Drivers.M1; package Driver_M1 renames Drivers.M1;-- Original Driver_M2 package still -- available via package renaming: with Drivers.M2; package Driver_M2 renames Drivers.M2;
Now, M1
and M2
are actual child packages of Drivers
, but
their original names are still available. By doing so, we ensure that existing
software that makes use of the original packages doesn't break.
Private packages
In this section, we discuss the concept of private packages. However, before we proceed with the discussion, let's recapitulate some important ideas that we've seen earlier.
In the Introduction to Ada course, we've seen that encapsulation plays an important role in modular programming. By using the private part of a package specification, we can disclose some information, but, at the same time, prevent that this information gets accessed where it shouldn't be used directly. Similarly, we've seen that we can use the private part of a package to distinguish between the partial and full view of a data type.
The main application of private packages is to create private child packages, whose purpose is to serve as internal implementation packages within a package hierarchy. By doing so, we can expose the internals to other public child packages, but prevent that external clients can directly access them.
As we'll see next, there are many rules that ensure that internal visibility is enforced for those private child packages. At the same time, the same rules ensure that private packages aren't visible outside of the package hierarchy.
Declaration and usage
We declare private packages by using the private
keyword. For example,
let's say we have a package named Data_Processing
:
package Data_Processing is -- ... end Data_Processing;
We simply write private package
to declare a private child package named
Calculations
:
private package Data_Processing.Calculations is -- ... end Data_Processing.Calculations;
Let's see a complete example:
package Data_Processing is type Data is private; procedure Process (D : in out Data); private type Data is null record; end Data_Processing;private package Data_Processing.Calculations is procedure Calculate (D : in out Data); end Data_Processing.Calculations;with Data_Processing.Calculations; use Data_Processing.Calculations; package body Data_Processing is procedure Process (D : in out Data) is begin Calculate (D); end Process; end Data_Processing;package body Data_Processing.Calculations is procedure Calculate (D : in out Data) is begin -- Dummy implementation... null; end Calculate; end Data_Processing.Calculations;with Data_Processing; use Data_Processing; procedure Test_Data_Processing is D : Data; begin Process (D); end Test_Data_Processing;
In this example, we refer to the private child package Calculations
in
the body of the Data_Processing
package — by simply writing
with Data_Processing.Calculations
. After that, we can call the
Calculate
procedure normally in the Process
procedure.
Private sibling packages
We can introduce another private package Advanced_Calculations
as a
child of Data_Processing
and refer to the Calculations
package
in its specification:
package Data_Processing is type Data is private; procedure Process (D : in out Data); private type Data is null record; end Data_Processing;private package Data_Processing.Calculations is procedure Calculate (D : in out Data); end Data_Processing.Calculations;with Data_Processing.Calculations; use Data_Processing.Calculations; private package Data_Processing.Advanced_Calculations is procedure Advanced_Calculate (D : in out Data) renames Calculate; end Data_Processing.Advanced_Calculations;with Data_Processing.Advanced_Calculations; use Data_Processing.Advanced_Calculations; package body Data_Processing is procedure Process (D : in out Data) is begin Advanced_Calculate (D); end Process; end Data_Processing;package body Data_Processing.Calculations is procedure Calculate (D : in out Data) is begin -- Dummy implementation... null; end Calculate; end Data_Processing.Calculations;with Data_Processing; use Data_Processing; procedure Test_Data_Processing is D : Data; begin Process (D); end Test_Data_Processing;
Note that, in the body of the Data_Processing
package, we're now
referring to the new Advanced_Calculations
package instead of the
Calculations
package.
Referring to a private child package in the specification of another private
child package is OK, but we cannot do the same in the specification of a
non-private package. For example, let's change the specification of the
Advanced_Calculations
and make it non-private:
with Data_Processing.Calculations; use Data_Processing.Calculations; package Data_Processing.Advanced_Calculations is procedure Advanced_Calculate (D : in out Data) renames Calculate; end Data_Processing.Advanced_Calculations;
Now, the compilation doesn't work anymore. However, we could still refer to
Calculations
packages in the body of the Advanced_Calculations
package:
package Data_Processing.Advanced_Calculations is procedure Advanced_Calculate (D : in out Data); end Data_Processing.Advanced_Calculations;with Data_Processing.Calculations; use Data_Processing.Calculations; package body Data_Processing.Advanced_Calculations is procedure Advanced_Calculate (D : in out Data) is begin Calculate (D); end Advanced_Calculate; end Data_Processing.Advanced_Calculations;
This works fine as expected: we can refer to private child packages in the body of another package — as long as both packages belong to the same package tree.
Outside the package tree
While we can use a with-clause of a private child package in the body of the
Data_Processing
package, we cannot do the same outside the package tree.
For example, we cannot refer to it in the Test_Data_Processing
procedure:
with Data_Processing; use Data_Processing; with Data_Processing.Calculations; use Data_Processing.Calculations; procedure Test_Data_Processing is D : Data; begin Calculate (D); end Test_Data_Processing;
As expected, we get a compilation error because Calculations
is only
accessible within the Data_Processing
, but not in the
Test_Data_Processing
procedure.
The same restrictions apply to child packages of private packages. For example,
if we implement a child package of the Calculations
package —
let's name it Calculations.Child
—, we cannot refer to it in the
Test_Data_Processing
procedure:
package Data_Processing.Calculations.Child is procedure Process (D : in out Data); end Data_Processing.Calculations.Child;package body Data_Processing.Calculations.Child is procedure Process (D : in out Data) is begin Calculate (D); end Process; end Data_Processing.Calculations.Child;with Data_Processing; use Data_Processing; with Data_Processing.Calculations.Child; use Data_Processing.Calculations.Child; procedure Test_Data_Processing is D : Data; begin Calculate (D); end Test_Data_Processing;
Again, as expected, we get an error because Calculations.Child
—
being a child of a private package — has the same restricted view as its
parent package. Therefore, it cannot be visible in the
Test_Data_Processing
procedure as well. We'll discuss more about
visibility later.
Note that subprograms can also be declared private. We'll see this in another section.
Important
We've discussed package renaming in a previous section. We can rename a package as a private package, too. For example:
package Driver_M1 is end Driver_M1;package Drivers with Pure is end Drivers;with Driver_M1; private package Drivers.M1 renames Driver_M1;
Obviously, Drivers.M1
has the same restrictions as any private
package:
with Driver_M1; with Drivers.M1; procedure Test_Driver is begin null; end Test_Driver;
As expected, although we can have the Driver_M1
package in a with
clause of the Test_Driver
procedure, we cannot do the same in the
case of the Drivers.M1
package because it is private.
In the Ada Reference Manual
Private with clauses
Definition and usage
A private with clause allows us to refer to a package in the private part of
another package. For example, if we want to refer to package P
in the
private part of Data
, we can write private with P
:
package P is type T is null record; end P;private with P; package Data is type T2 is private; private -- Information from P is -- visible here type T2 is new P.T; end Data;with Data; use Data; procedure Main is A : T2; begin null; end Main;
As you can see in the example, as the information from P
is available in
the private part of Data
, we can derive a new type T2
based on
T
from P
. However, we cannot do the same in the visible part of
Data
:
private with P; package Data is -- ERROR: information from P -- isn't visible here type T2 is new P.T; end Data;
Also, the information from P
is available in the package body. For
example, let's declare a Process
procedure in the P
package and
use it in the body of the Data
package:
package P is type T is null record; procedure Process (A : T) is null; end P;private with P; package Data is type T2 is private; procedure Process (A : T2); private -- Information from P is -- visible here type T2 is new P.T; end Data;package body Data is procedure Process (A : T2) is begin P.Process (P.T (A)); end Process; end Data;with Data; use Data; procedure Main is A : T2; begin null; end Main;
In the body of the Data
, we can access information from the P
package — as we do in the P.Process (P.T (A))
statement of the
Process
procedure.
Referring to private child package
There's one case where using a private with clause is the only way to refer to
a package: when we want to refer to a private child package in another child
package. For example, here we have a package P
and its two child
packages: Private_Child
and Public_Child
:
package P is end P;private package P.Private_Child is type T is null record; end P.Private_Child;private with P.Private_Child; package P.Public_Child is type T2 is private; private type T2 is new P.Private_Child.T; end P.Public_Child;with P.Public_Child; use P.Public_Child; procedure Test_Parent_Child is A : T2; begin null; end Test_Parent_Child;
In this example, we're referring to the P.Private_Child
package in the
P.Public_Child
package. As expected, this works fine. However, using a
normal with clause doesn't work in this case:
with P.Private_Child; package P.Public_Child is type T2 is private; private type T2 is new P.Private_Child.T; end P.Public_Child;
This gives an error because the information from the P.Private_Child
,
being a private child package, cannot be accessed in the public part of another
child package. In summary, unless both packages are private packages, it's only
possible to access the information from a private package in the private part
of a non-private child package.
In the Ada Reference Manual
Limited Visibility
Sometimes, we might face the situation where two packages depend on
information from each other. Let's consider a package A
that depends
on a package B
, and vice-versa:
with B; use B; package A is type T1 is record Value : T2; end record; end A;with A; use A; package B is type T2 is record Value : T1; end record; end B;
Here, we have two
mutually dependent types T1
and T2
, which are declared in two packages A
and B
that
refer to each other. These with clauses constitute a circular dependency, so
the compiler cannot compile either of those packages.
One way to solve this problem is by transforming this circular dependency into
a partial dependency. We do this by limiting the visibility — using a
limited with clause. To use a limited with clause for a package P
, we
simply write limited with P
.
If a package A
has limited visibility to a package B
, then all
types from package B
are visible as if they had been declared as
incomplete types. For the specific case of
the previous source-code example, this would be the limited visibility to
package B
from package A
's perspective:
package B is
-- Incomplete type
type T2;
end B;
As we've seen previously,
we cannot declare objects of incomplete types, but we can declare access types and anonymous access objects of incomplete types. Also,
we can use anonymous access types to declare mutually dependent types.
Keeping this information in mind, we can now correct the previous code by using
limited with clauses for package A
and declaring the component of the
T1
record using an anonymous access type:
limited with B; package A is type T1 is record Ref : access B.T2; end record; end A;with A; use A; package B is type T2 is record Value : T1; end record; end B;
As expected, we can now compile the code without issues.
Note that we can also use limited with clauses for both packages. If we do that, we must declare all components using anonymous access types:
limited with B; package A is type T1 is record Ref : access B.T2; end record; end A;limited with A; package B is type T2 is record Ref : access A.T1; end record; end B;
Now, both packages A
and B
have limited visibility to each other.
In the Ada Reference Manual
Limited visibility and private with clauses
We can limit the visibility and use
private with clauses at the same time.
For a package P
, we do this by simply writing
limited private with P
.
Let's reuse the previous source-code example and convert types T1
and
T2
to private types:
limited private with B; package A is type T1 is private; private -- Here, we have limited visibility -- of package B type T1 is record Ref : access B.T2; end record; end A;private with A; package B is type T2 is private; private use A; -- Here, we have full visibility -- of package A type T2 is record Value : T1; end record; end B;
In this updated version of the source-code example, we have not only limited
visibility to package B
, but also, each package is just visible
in the private part of the other package.
Limited visibility and other elements
It's important to mention that the limited visibility we've been discussing so
far is restricted to type declarations — which are seen as incomplete
types. In fact, when we use a limited with clause, all other declarations have
no visibility at all! For example, let's say we have a package Info
that
declares a constant Zero_Const
and a function Zero_Func
:
package Info is function Zero_Func return Integer is (0); Zero_Const : constant := 0; end Info;
Also, let's say we want to use the information (from package Info
) in
package A
. If we have limited visibility to package Info
,
however, this information won't be visible. For example:
limited private with Info; package A is type T1 is private; private type T1 is record V : Integer := Info.Zero_Const; W : Integer := Info.Zero_Func; end record; end A;
As expected, compilation fails because of the limited visibility — as
Zero_Const
and Zero_Func
from the Info
package are not
visible in the private part of A
. (Of course, if we revert to full
visibility by simply removing the limited
keyword from the example, the
code compiles just fine.)
Visibility
In the previous sections, we already discussed visibility from various angles. However, it can be interesting to recapitulate this information with the help of diagrams that illustrate the different parts of a package and its relation with other units.
Automatic visibility
First, let's consider we have a package A
, its children (A.G
and
A.H
), and the grandchild A.G.T
. As we've seen before, information
of a parent package is automatically visible in its children. The following
diagrams illustrates this:
Because of this automatic visibility, many with clauses would be redundant in
child packages. For example, we don't have to write
with A; package A.G is
, since the specification of package A
is
already visible in its child packages.
If we focus on package A.G
(highlighted in the figure above), we see
that it only has automatic visibility to its parent A
, but not its child
A.G.T
. Also, it doesn't have visibility to its sibling A.H
.
With clauses and visibility
In the rest of this section, we discuss all the situations where using with
clauses is necessary to access the information of a package. Let's consider
this example where we refer to a package B
in the specification of a
package A
(using with B
):
As we already know, the information from the public part of package B
is
visible in the public part of package A
. In addition to that, it's also
visible in the private part and in the body of package A
. This is
indicated by the dotted green arrows in the figure above.
Now, let's see the case where we refer to package B
in the private
part of package A
(using private with B
):
Here, the information is visible in the private part of package A
, as
well as in its body. Finally, let's see the case where we refer to
package B
in the body of package A
:
Here, the information is only visible in the body of package A
.
Circular dependency
Let's return to package A
and its descendants. As we've seen in previous
sections, we cannot refer to a child package in the specification of its parent
package because that would constitute circular dependency. (For example, we
cannot write with A.G; package A is
.) This situation — which
causes a compilation error — is indicated by the red arrows in the figure
below:
Note that referring to the child package A.G
in the body of its parent
is perfectly fine.
Private packages
The previous examples of this section only showed public packages. As we've
seen before, we cannot refer to private packages outside of a package
hierarchy, as we can see in the following example where we try to refer to
package A
and its descendants in the Test
procedure:
As indicated by the red arrows, we cannot refer to the private child packages
of A
in the Test
procedure, only the public child packages.
Within the package hierarchy itself, we cannot refer to the private package
A.G
in public sibling packages. For example:
Here, we cannot refer to the private package A.G
in the public package
A.H
— as indicated by the red arrow. However, we can refer to the
private package A.G
in other private packages, such as A.I
— as indicated by the green arrows.
Use type clause
Back in the Introduction to Ada course, we saw that use clauses provide direct visibility — in the scope where they're used — to the content of a package's visible part.
For example, consider this simple procedure:
with Ada.Text_IO; procedure Display_Message is begin Ada.Text_IO.Put_Line ("Hello World!"); end Display_Message;
By adding use Ada.Text_IO
to this code, we make the visible part of the
Ada.Text_IO
package directly visible in the scope of the
Display_Message
procedure, so we can now just write Put_Line
instead of Ada.Text_IO.Put_Line
:
with Ada.Text_IO; use Ada.Text_IO; procedure Display_Message is begin Put_Line ("Hello World!"); end Display_Message;
In this section, we discuss another example of use clauses. In addition, we
introduce two specific forms of use clauses: use type
and
use all type
.
In the Ada Reference Manual
Another use clause example
Let's now consider a simple package called Points
, which contains the
declaration of the Point
type and two primitive: an Init
function
and an addition operator.
package Points is type Point is private; function Init return Point; function "+" (P : Point; I : Integer) return Point; private type Point is record X, Y : Integer; end record; function Init return Point is (0, 0); function "+" (P : Point; I : Integer) return Point is (P.X + I, P.Y + I); end Points;
We can implement a simple procedure that makes use of this package:
with Points; use Points; procedure Show_Point is P : Point; begin P := Init; P := P + 1; end Show_Point;
Here, we have a use clause, so we have direct visibility to the content of
Points
's visible part.
Visibility and Readability
In certain situations, however, we might want to avoid the use clause. If
that's the case, we can rewrite the previous implementation by removing the use
clause and specifying the Points
package in the prefixed form:
with Points; procedure Show_Point is P : Points.Point; begin P := Points.Init; P := Points."+" (P, 1); end Show_Point;
Although this code is correct, it might be difficult to read, as we have to
specify the package whenever we're referring to a type or a subprogram from
that package. Even worse: we now have to write operators in the prefixed form
— such as Points."+" (P, 1)
.
use type
As a compromise, we can have direct visibility to the operators of a certain
type. We do this by using a use clause in the form use type
. This allows
us to simplify the previous example:
with Points; procedure Show_Point is use type Points.Point; P : Points.Point; begin P := Points.Init; P := P + 1; end Show_Point;
Note that use type
just gives us direct visibility to the operators of a
certain type, but not other primitives. For this reason, we still have to write
Points.Init
in the code example.
use all type
If we want to have direct visibility to all primitives of a certain type (and
not just its operators), we need to write a use clause in the form
use all type
. This allows us to simplify the previous example even
further:
with Points; procedure Show_Point is use all type Points.Point; P : Points.Point; begin P := Init; P := P + 1; end Show_Point;
Now, we've removed the prefix from all operations on the P
variable.
Use clauses and naming conflicts
Visibility issues may arise when we have multiple use clauses. For instance, we might have types with the same name declared in multiple packages. This constitutes a naming conflict; in this case, the types become hidden — so they're not directly visible anymore, even if we have a use clause.
In the Ada Reference Manual
Code example
Let's start with a code example. First, we declare and implement a generic
procedure that shows the value of a Complex
object:
with Ada.Numerics.Generic_Complex_Types; generic with package Complex_Types is new Ada.Numerics.Generic_Complex_Types (<>); procedure Show_Any_Complex (Msg : String; Val : Complex_Types.Complex);with Ada.Text_IO; with Ada.Text_IO.Complex_IO; procedure Show_Any_Complex (Msg : String; Val : Complex_Types.Complex) is package Complex_Float_Types_IO is new Ada.Text_IO.Complex_IO (Complex_Types); use Complex_Float_Types_IO; use Ada.Text_IO; begin Put (Msg & " "); Put (Val); New_Line; end Show_Any_Complex;
Then, we implement a test procedure where we declare the
Complex_Float_Types
package as an instance of the
Generic_Complex_Types
package:
with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use Complex_Float_Types; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); C, D, X : Complex; begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); end Show_Use;
In this example, we declare variables of the Complex
type, initialize
them and use them in operations. Note that we have direct visibility to the
package instance because we've added a simple use clause after the package
instantiation — see use Complex_Float_Types
in the example.
Naming conflict
Now, let's add the declaration of the Complex_Long_Float_Types
package
— a second instantiation of the Generic_Complex_Types
package
— to the code example:
with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use Complex_Float_Types; package Complex_Long_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Long_Float); use Complex_Long_Float_Types; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); C, D, X : Complex; -- ^ ERROR: type is hidden! begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); end Show_Use;
This example doesn't compile because we have direct visibility to both
Complex_Float_Types
and Complex_Long_Float_Types
packages, and
both of them declare the Complex
type. In this case, the type
declaration becomes hidden, as the compiler cannot decide which declaration of
Complex
it should take.
Circumventing naming conflicts
As we know, a simple fix for this compilation error is to add the package prefix in the variable declaration:
with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use Complex_Float_Types; package Complex_Long_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Long_Float); use Complex_Long_Float_Types; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); C, D, X : Complex_Float_Types.Complex; -- ^ SOLVED: package is now specified. begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); end Show_Use;
Another possibility is to write a use clause in the form use all type
:
with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use all type Complex_Float_Types.Complex; package Complex_Long_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Long_Float); use all type Complex_Long_Float_Types.Complex; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); C, D, X : Complex_Float_Types.Complex; begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); end Show_Use;
For the sake of completeness, let's declare and use variables of both
Complex
types:
with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use all type Complex_Float_Types.Complex; package Complex_Long_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Long_Float); use all type Complex_Long_Float_Types.Complex; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); procedure Show_Complex_Long_Float is new Show_Any_Complex (Complex_Long_Float_Types); C, D, X : Complex_Float_Types.Complex; E, F, Y : Complex_Long_Float_Types.Complex; begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); E := Compose_From_Polar (3.0, Pi / 2.0); F := Compose_From_Polar (5.0, Pi / 2.0); Y := E + F; Show_Complex_Long_Float ("E:", E); Show_Complex_Long_Float ("F:", F); Show_Complex_Long_Float ("Y:", Y); end Show_Use;
As expected, the code compiles correctly.