Ada: Re-exporting enum type values

Viewed 204

I have an ada package in the form of

package C is
   type Test is (Test_1, Test_2, Test_3);   
end C;

and another package in the form of

with C;
package B is
   subtype Test is C.Test;
end B;

and yet another in the form of

with B;
package A is
   subtype Test is B.Test;   
   Item: Test := Test_1;
end A;

Naively, I would expect that a subtype defined in B, which is later subtyped by A, would be able to access members of the original type. However, upon inspection, the members of C are not even visible in the scope of B. This can be fixed by adding use c;, this seems like a find solution to a degree, however to use it in A, you would have to add with c; use c; to every package that transitively depends on C. This could lead to confusion as it wouldn't be implicitly obvious that you should be using C.

I would like to be able to "rexport" these types, so that I can abstract layers of my program better.

4 Answers

If you change package A to

with B;
package A is
   subtype Test is B.Test;
   use all type Test;
   Item: Test := Test_1;
end A;

the code compiles. Not sure whether this will help.

This is an Ada 2012 feature; see ARM 8.4(8) and (8.1). use all type makes the primitive operations of the type visible (which includes enumeration literals).

If you really want to re-export the values, you can do this:

with C;
package B is
   subtype Test is C.Test;
   function Test_1 return Test is (C.Test_1);
   function Test_2 return Test is (C.Test_2);
   function Test_3 return Test is (C.Test_3); 
end B;

Unfortunately, you can't use named numbers since enumerations are not numeric types. You can make these functions normal constants instead, but conceptually, this would execute code at elaboration time (the compiler will probably optimize it away, but you can't use pragma Preelaborate anymore).

This allows you to access the literals in A using B.Test_1 etc. This is also a proper abstraction as A will not depend on the literals defined in C anymore (you can rename the literals in C without affecting A, you do need to update B though to map the new names to the original ones).

Simply importing the literal names into the namespace of A is not an abstraction.

with B;
package A is
   type Test is new B.Test;   
   Item: Test := Test_1;
end A;

...and all is well!

Ada has the concept of visibility, with a lot of rules that define when, where, and how things are visible. Understanding visibility is key to understanding Ada. One of the best discussions of visibility is in Ada Distilled. What you're encountering is a consequence of the visibility rules. This goes even further than enumeration literals:

package Pack is
   type Int is range -(2 ** 15) + 1 .. 2 ** 15 - 1;
end Pack;

with Pack;
procedure Proc is
   subtype Int is Pack.Int;
   -- None of the operators of Int ("+", "*", ...) are directly visible here
begin
   null;
end Proc;

Declarations in a package spec are only visible outside the package by using dotted notation, unless you explicitly make them directly visible elsewhere. use clauses are one way to make them visible; others are renames and, for enumeration literals, declaring constants of the type initialized using dotted notation.

Related