Say I have package P1 with an enumerated type Enum defined there.
package P1 is
type Enum is (a, b);
end;
Then a package P2 which subtypes P1.Enum:
with P1;
package P2 is
subtype Enum is P1.Enum;
end;
Then a procedure P3 which in its turn subtypes P2.Enum:
with P2;
procedure P3 is
subtype Enum is P2.Enum;
x : Enum;
begin
x := a;
end;
Out of architectural constraints, P3 can reference P2, P2 can reference P1, but P3 is not allowed to know of P1.
The issue is that a is not visible to P3.
GNAT complains rightfully:
p3.adb:6:08: "a" is not visible
p3.adb:6:08: non-visible declaration at p1.ads:2
… and so does HAC:
p3.adb: 6:9-9: undefined identifier: a
The question now: is there a more or less elegant way of making the enumeration items a, b available to P3 ?
You can use P3.Enum'First to get it, though this is simply because a is the first item. If this is all you need, then use it.
However, if you actually need various values, then you can use “private constants” —I’m drawing a blank on the actual LRM term— but you use this like:
with P1;
Package P2 is
subtype Enum is P1.Enum;
Interesting_Value : Constant Enum;
Meaningful_Value : Constant Enum;
Private
Interesting_Value : Constant Enum:= P1.A;
Meaningful_Value : Constant Enum:= P1.B;
End P2;
Thus you can retrieve the values, albeit renamed, from P2.
Now, as to the reason for this: Ada treats enum value-names as a parameterless function returning that particular value — so, just as you cannot use subprograms from P1 without withing it, you cannot use the named values without withing it.
Note: this does mean that you could rewrite the “private constants” as functions:
with P1;
Package P2 is
subtype Enum is P1.Enum;
Function Interesting_Value return Enum renames P1.A;
Function Meaningful_Value return Enum renames P1.B;
End P2;
You can rename literals in P2 if they are not literals:
A : E renames P1.A;
That does not work with character literals.
Though literals are functions in Ada, they are not primitive functions. Primitive operations require no visibility, they cannot be hidden. Compare this with numeric literals. They are kind of primitive functions being of a universal type (universal_real or universal_integer) and “inherited” per conversion to the specific type. This is why you need no acrobatic with them.
For myself, I have turned to child and grand child packages in lieu of ‘with’ and ‘use’, in many cases, finding the latter a little troublesome for my limited experience with Ada. But I might revisit that design. This would give us:
package P1 is
type Enum is (a,b);
end;
package p1.2 is
– type Enum is inherited; if physically separate file, p1.2 is p1-2 in file --system, but in Ada named p1.2
procedure p3 is
begin – within p1.2
x :=a; – I don’t think you need the parent package prefix of p1.x :=a
end procedure p3; ---------
– but what is x? It isn’t declared as a type enum and can you change enum’s set fixed terms? Is there an Ada type for elements of an enumeration type or are they just a vague name reference for if and case statements, and not really a type in themselves ?
Try "put(enum.a) or a variation, 'image, 'first, etc. ;"That would be a must, in order to change an element in an enumeration type, wouldn’t if? And if so, the we need a variable instance name for the enumeration type’s elements, a and b. a and b are not variables, are they, but fixed constants, the basic concept of an enumeration type.
I could easily be wrong, but are you assigning an enumeration type’s element to a separate enumeration type’s name? That would be two distinct and incompatable types, unless you did an explicit type conversion of some sort.
Barne’s Ada 2012 has a good section on overloading enumeration type’s elements, with the enumeration type name.
type color is (blue, red, black);
type mood is (blue, red, black);
…
– this seems crude, but it works
if color = blue then put (“it’s a beautiful blue sky today”); end if;
if mood =blue then put(“I am feeling blue today”); end if;
Ada’s strick rules on “everything is a type” seems fuzzy on the type of an enumeration type’s elements, which have to be inferred and seems mostly a convenience for case and if statements, to me.
Thanks; I was not clear that an enumeration type’s literals were a parameterless function returning the actual value; doesn’t the function’s return type have to returned to the same type variable as the returned type? Do you mean return is parameterless? In this case an enumeration literal So, contrary to all of Ada strict typing, we here have an x (without a prior declaration) implicitly becoming a literal type of an element of an enumeration type. Some little hidden feature of Ada standards and compiler functions/
I’m not sure what you mean here, Ada allows function overloading.
If you were to ‘privatize’ the original types it would be:
Package Example is
Type Color is private;
Function Blue return Color;
Function Red return Color;
Function Black return Color;
Type Mood is private;
Function Blue return Mood;
Function Red return Mood;
Function Black return Mood;
Private
-- Here the values are given arbitrary names, for illustrative purposes.
Type Color is (Name_A, Name_B, Name_C);
Type Mood is (Name_1, Name_2, Name_3);
-- …also, we are explicitly showing the values here, assuming a system
-- bit-width of 4, for simplicity's sake.
For Color use (
Name_A => 2#0000#, -- Color'Pos( Name_A ) = 0.
Name_B => 2#0001#, -- Color'Pos( Name_B ) = 1.
Name_C => 2#0010# -- Color'Pos( Name_C ) = 2.
); -- 'Pos realized internally via Unchecked_Conversion.
Function Blue return Color renames Name_A;
Function Red return Color renames Name_B;
Function Black return Color renames Name_C;
Function Blue return Mood renames Name_1;
Function Red return Mood renames Name_2;
Function Black return Mood renames Name_3;
End Example;
So, the above is essentially the same as the definition you gave, albeit w/o the attributes being exposed to the user Example.
And, for completeness, the reason the if color_value = blue and if mood_value = blue works is because of overloading: the = that is used is either Function "=" (Left, Right : Color) return Boolean or Function "=" (Left, Right : Mood) return Boolean. Since ‘Blue’ could be either, Right is indeterminate, so the compiler looks at Left and there it finds a single = which it could be, which then forces the Right parameter to be Mood, which disambiguates the call of Blue.
No, you have it backwards: the strict typing is what allows us to ‘work backwards’ and disambiguate the overloaded function so that we know what everything is.