Search code examples
genericspackageadaelaboration

Can I instantiate a generic within the same unit in Ada?


It doesn't seem like this is possible, but I haven't seen a definitive answer. What I want to do is define a generic subprogram with some generic formal parameters and instantiate it in the same package, like the following simplified and untested example:

generic
    Proc_Address : access System.Address;
    type Param_1_Type (<>) is private;
procedure Procedure_IP(Param_1 : Param_1_Type);

Instance_1_Address : System.Address := ...
procedure Instance_1 is new Procedure_IP(Instance_1_Address, type_1);
Instance_2_Address : System.Address := ...
procedure Instance_2 is new Procedure_IP(Instance_2_Address, type_2);
--etc

But this kind of thing keeps resulting in those "access before elaboration" errors. I can't seem to find any pragmas that will affect elaboration of a single subprogram; seems it has to be the whole package. Attempts to move the generic subprogram into a separate package have proven more troublesome than I had hoped because the specific stuff I want the function to do is closely related to other things happening in the same package.

Is there any way to resolve the elaboration order issue, so that I can keep the declaration and instantiations in the same package?

Option B is to rework this in a different way, like maybe passing a function pointer to a non-generic function or something, but doing it as a generic seems to be the cleanest way to go about it, especially since I'll need to refer to each of the intended instances a lot.


Solution

  • So the package or subprogram body must be complete before you can instantiate a generic. That said, you can use a package hierarchy to address the issue of needing to share data between the main package and the procedure if in a different file. Ada provides "private" child packages that can be shared among its siblings. You essentially do a hierarchy like this:

                                         Top_Level
                                              |
              ----------------------------------------------------------------
              |                               |                              |
    Top_Level.Main_Package        Top_Level.Generic_Procedure     Top_Level.Private_Package
    

    Then both Main_Package and Generic_Procedure have access to things in Private_Package and you can instantiate Generic_Procedure in Main_Package. Example Code to follow is below. Notice how both Main_Package and Generic_Procedure can access Thing in Private_Package and since the package is private, only they can use it.

    top_level.ads

    package Top_Level with Pure is end Top_Level;
    

    top_level-main_package.ads

    with Top_Level.Generic_Procedure;
    with System;
    private with Top_Level.Private_Package;
    
    package Top_Level.Main_Package is
    
        subtype type_1 is Integer;
        subtype type_2 is String;
        
        Instance_1_Address : System.Address := System.Null_Address;
        procedure Instance_1 is new Generic_Procedure(Instance_1_Address, type_1);
        Instance_2_Address : System.Address := System.Null_Address;
        procedure Instance_2 is new Generic_Procedure(Instance_1_Address, type_2);
    
        function Get_Thing return Integer;
    
    private
    
        function Get_Thing return Integer is (Private_Package.Thing);
    
    end Top_Level.Main_Package;
    

    top_level-generic_procedure.ads

    with System;
    
    generic
        Proc_Address : System.Address;
        type Param_1_Type (<>) is private;
    procedure Top_Level.Generic_Procedure(Param_1 : Param_1_Type);
    

    top_level-generic_procedure.adb

    with Top_Level.Private_Package;
    with Ada.Text_IO;
    
    procedure Top_Level.Generic_Procedure
        (Param_1 : Param_1_Type) 
    is
    begin
        Ada.Text_IO.Put_Line(Private_Package.Thing'Image);
    end Top_Level.Generic_Procedure;
    

    top_level-private_package.ads

    private package Top_Level.Private_Package is
    
        Thing : constant Integer := 3;
        
    end Top_Level.Private_Package;
    

    Full compiled example on godbolt: https://godbolt.org/z/7qaaPc4Ex