Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you store (an access to) Integer's operators in Ada?

Tags:

ada

In Ada, the context can determine that "+" is not a String but an integer operator, as in the expression: "+"(5,2). The question is, how do I store that operator in a variable? I want to pass that integer operator, or some other one, as a binary function taking two Integers and returning an Integer. In the code below, I made an explicit function that just calls the operator, which I can use as a workaround. Is there some way to avoid having this wrapper, and pass around (an access to) Integer's "+" operator directly?

with Ada.Text_IO; use Ada.Text_IO;

procedure operator is

  type binary_int_operator is access function(lhs : Integer; rhs : Integer) return Integer;

  --plus : binary_int_operator := Integer."+"'Access;
  --plus : binary_int_operator := Integer'Access("+");
  --plus : binary_int_operator := Integer'"+";
  --plus : binary_int_operator := "+";

  function plus(lhs : Integer; rhs : Integer) return Integer is
  begin
    return lhs + rhs;
  end plus;

begin
  Put_Line(Integer'Image("+"(5, 12)));
end operator;

The commented declarations show some attempts I made, which do not compile.

like image 788
TamaMcGlinn Avatar asked Jun 13 '19 19:06

TamaMcGlinn


2 Answers

I'm afraid you can't do that. The "+" subprogram for Integer is defined in the package Standard [ARM A.1 (17)] and therefore intrinsic [AARM A.1 (2.a)]. It's not allowed to reference an intrinsic subprogram [ARM 3.10.2 (32.3)]. Hence, compiling the program

procedure Main is

   type Binary_Int_Operator is
     access function (lhs : Integer; rhs : Integer) return Integer;

   Plus : Binary_Int_Operator := Standard."+"'Access;

begin
   null;
end Main;

yields

6:34 prefix of "Access" attribute cannot be intrinsic

The only workaround is using an indirection. This program compiles

with Ada.Text_IO;         use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure Main_Alt is

   type Operation is
     access function (Lhs, Rhs : Integer) return Integer;


   --  Sticking to "+" and "-" instead of names like Add or Subtract
   --  to demonstrate that you can reference operator subprograms
   --  (using the Access attribute) as long as they're not intrinsic.

   function "+" (Lhs, Rhs : Integer) return Integer is
     (Standard."+" (Lhs, Rhs));

   function "-" (Lhs, Rhs : Integer) return Integer is
     (Standard."-" (Lhs, Rhs));


   procedure Calc_And_Show (Lhs, Rhs : Integer; Op : Operation) is
   begin
      Put (Op (lhs, rhs));
      New_Line;
   end Calc_And_Show;

begin
   Calc_And_Show (5, 3, "+"'Access); 
   Calc_And_Show (5, 3, "-"'Access);
end Main_Alt;

and yields (as expected)

$ ./main_alt
          8
          2
like image 106
DeeDee Avatar answered Oct 12 '22 07:10

DeeDee


I would suggest considering a different approach using generics. Generally, I think you end up with a simpler interface for the call then you get trying to pass in access to subprogram parameters. (i.e. no need to pass the operation for each call).

Using generics, you don't need to use 'Access at all, and you can pass intrinsic functions such as integer "+", as formal generic parameters.

with Ada.Text_IO;         use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure Main is

   generic
      with function Op (L, R : Integer) return Integer;
   procedure Calc_And_Show (Lhs, Rhs : Integer);

   procedure Calc_And_Show (Lhs, Rhs : Integer) is
   begin
      Put (Op (lhs, rhs));
      New_Line;
   end Calc_And_Show;

   procedure Calc_And_Show_Plus is new Calc_And_Show (Op => "+");
   procedure Calc_And_Show_Minus is new Calc_And_Show (Op => "-");

begin
   Calc_And_Show_Plus  (5, 3); 
   Calc_And_Show_Minus (5, 3);   
end Main;

There might be reasons why you'd want to use access parameters instead, such as if you wanted Calc_And_Show to be callable from other languages such as C, or if you are in a nested level of code and all you have passed to your nested level is an access to subprogram value. But I think it's generally a good idea to otherwise use generics or at least consider that option as a first preference, unless you have good reason not to.

like image 37
B. Moore Avatar answered Oct 12 '22 06:10

B. Moore