$ gnatmake example.adb
gcc -c example.adb
example.adb:16:17: error: ambiguous expression (cannot resolve "New_B")
example.adb:16:17: error: possible interpretation at pak.ads:8
example.adb:16:17: error: possible interpretation at pak.ads:6
gnatmake: "example.adb" compilation error
pak.ads
package Pak is
type A is abstract tagged private;
type B is new A with private;
function New_B return B;
type C is new B with private;
function New_C return C;
private
type A is abstract tagged record
null;
end record;
type B is new A with null record;
type C is new B with null record;
end Pak;
pak.adb
package body Pak is
function New_B return B is
begin
return (others => <>);
end New_B;
function New_C return C is
begin
return (others => <>);
end New_C;
end Pak;
example.adb
with Ada.Containers.Indefinite_Vectors;
with Pak;
procedure Example is
use type Pak.A; -- compiler insists for this
package Pak_Vectors is new Ada.Containers.Indefinite_Vectors
(Index_Type => Positive,
Element_Type => Pak.A'Class);
subtype Vec is Pak_Vectors.Vector;
V : Vec;
begin
V.Append (Pak.New_B);
V.Append (Pak.New_C);
end Example;
C inherits New_B from B. Thus you have two overloaded functions:
function New_B return B;
and
function New_B return C;
both can be converted to A’Class. So the error goes.
P.S. In Ada 95 it was a bug because you had to explicitly override a covariant function since there is no safe way to inherit it. Later this rule was relaxed for degenerate cases like when the extension is null record (your case).
In my view it was language design bug. If the compiler had rejected your code, you would probably redesign it. Having New_B and New_C is C rather than Ada. You should have one constructing primitive function working for all class.
Thanks, I suspected it was caused by the second level of inheritance.
The above example is dumbed down to distill the problem, the actual case looks more like:
type A is abstract tagged private;
-- ... some abstract subprograms of A
type B is new A with null record;
function New_B return B;
type C is new A with private; -- additional components in full view
function New_C (P : Param) return C;
type D is new A with private; -- likewise
function New_D return D;
type E is new A with private; -- etc.
function New_E (P : Param) return E;
type F is new E with private;
function New_F (P : Param) return F;
[ A ] abstract
|
__________________________
| | | |
[ B ] [ C ] [ D ] [ E ]
|
[ F ]
So descendant types inherit some data component, implement the abstract subprograms, but may add individual record components and may even ask for some parameters in the constructor subprogram.
I don’t know if it’s possible to avoid making a separate New_* constructor for each descendant.
I need those types to be constructible without much ceremony and if one type gets such a constructor, for symmetry all others should get it.
Please, if you have some example of how it could be done using inheritance, leave a link to it.
As I see it, I can just make F descend from A and compose E into its record.
That way I’m acknowledging defeat, but at least it will compile.
You already have aggregates for that. But if you want a constructing function that is:
type A is abstract tagged private;
function Create [(...)] return A is abstract;
When you create A’Class you need to tell what type it should be, e.g. using a qualified expression or a dispatching constructor. It is like dispatching, but you have no object yet to tell the tag.
If you want type-specific constructing functions then they must be contravariant, i.e. non-primitive. There are two ways of doing this:
Class wide function New_B return A'Class;
Declaring after the type freezing, e.g. in a separate package.