How to declare access to any subprogram formal type in generic?

Viewed 159

I need to make a generic package, the formal parameter of which should be the type of access to subprogram. An object of this type is passed to the subroutine of this package and I have to check it for null equality.

generic
   type Func_Type is private; 
package Gen_Package is
   function Check(Func : Func_Type) return Boolean is
     (Func = null);
end;

But this not compile, since the left operand in comparison does not have an access type.

3 Answers

You can make use of the fact that the initial value for objects of access types is null:

generic
   type Func_Type is private;
   --  make comparison operator available. `is <>` means it does not
   --  need to be given explicitly if it is visible at the site of instantiation.
   with function "=" (Left, Right : Func_Type) return Boolean is <>;
package Gen_Package is
   function Check (Func : Func_Type) return Boolean;
end Gen_Package;

package body Gen_Package is
   function Check (Func : Func_Type) return Boolean is
      --  not constant because we cannot give an initialization expression.
      --  if Func_Type is an access type, Null_Value will be initialized with null.
      Null_Value : Func_Type;
   begin
      return Func = Null_Value;
   end Check;
end Gen_Package;

You should specify the access-to-subprogram type you need as your formal parameter. Ada Language Reference Manual section 12.5.4 states: "For a formal access-to-subprogram subtype, the designated profiles of the formal and the actual shall be subtype conformant."

This leads to declaring something like the following:

generic
   type func_type is access function(S : String) return string;
   gen_func : not null func_type;
   type proc_type is access procedure (Msg : in String);
   gen_proc : not null proc_type;
package test_subprog is
   procedure Print;
end test_subprog;

You must declare both the access to subprogram type and a formal parameter for the corresponding access to subprogram instance. If you specify that the formal parameter must be "not null" then you need not test for a null parameter in your code.

with Ada.Text_IO; use Ada.Text_IO;

package body test_subprog is

   -----------
   -- Print --
   -----------

   procedure Print is
      Msg : string := "This is a test";
   begin
      Put_Line(Item => gen_func(Msg));
      gen_proc(gen_func(Msg));
   end Print;

end test_subprog;

Calling the formal access to subprograms passed as generic parameters takes no special syntax. Making an instance of the actual parameters to use the generic package can be done as:

with Ada.text_io; use Ada.Text_IO;
with test_subprog;

procedure Main is
   function format(S : String) return String is
   begin
      return "Called through function access " & S;
   end format;
   
   type format_access is access function(S : string) return String;
   
   procedure reformat(S : String) is
   begin
      Put_Line("Called through procedure access " & S);
   end reformat;
   
   type proc_access is access procedure (S : String);
   
   package sub_test is new test_subprog(func_type => format_access,
                                        gen_func  => format'access,
                                        proc_type => proc_access,
                                        gen_proc  => reformat'access);
begin
   sub_test.print;
end Main;

This convoluted approach allows you to use access to subprogram types as generic parameters. Unless you actually need an access to subprogram type one can simply use a subprogram formal parameter such as is described in the Ada Language Reference Manual section 12.6:

Examples of generic formal subprograms:

with function "+"(X, Y : Item) return Item is <>;
with function Image(X : Enum) return String is Enum'Image;
with procedure Update is Default_Update;
with procedure Pre_Action(X : in Item) is null;  -- defaults to no action
with procedure Write(S    : not null access Root_Stream_Type'Class;
                     Desc : Descriptor)
                     is abstract Descriptor'Write;  -- see 13.13.2
-- Dispatching operation on Descriptor with default
--  given the generic procedure declaration 
generic
   with procedure Action (X : in Item);
procedure Iterate(Seq : in Item_Sequence);
--  and the procedure 
procedure Put_Item(X : in Item);
--  the following instantiation is possible 
procedure Put_List is new Iterate(Action => Put_Item);

In generic packet will also be passed a function that accepts this type.

Perhaps something like

generic -- GP
   type Func_Ptr (<>) is limited private; -- Expected to be an access type

   with function Is_Null (Ptr : in Func_Ptr) return Boolean;

   with procedure Use_It (Ptr : in Func_Ptr);
package GP is
   procedure Checked_Use (Ptr : in Func_Ptr);
end GP;

package body GP is
   procedure Checked_Use (Ptr : in Func_Ptr) is
      -- Nothing here
   begin -- Checked_Use
      if not Is_Null (Ptr) then
         Use_It (Ptr => Ptr);
      else
         -- Handle null ptr
      end if;
   end Checked_Use;
end GP;

?

Without more information about what you intend, it's hard to be helpful. Generally, when you find yourself fighting the type system, it's an indication that you have a design problem.

Related