1

Say I have the following type:

type Example_Type is new Float;

A large number of intrinsic operators will be defined for Example_Type, which is good in the overwhelming majority of cases. Like:

function "+"(Left, Right : Example_Type) return Example_Type;

and

function "*"(Left, Right : Example_Type) return Example_Type;

But let's say multiplication of two Example_Type's shouldn't equal another Example_Type, like what occurs with Dimensions. How can these intrinsic operators be hidden selectively?

Patrick Kelly
  • 633
  • 5
  • 22

3 Answers3

6

You make the disallowed operations abstract:

procedure Disallow is
   type Length is digits 10;
   function "*" (Left, Right : Length) return Length is abstract;

   A, B, C, D : Length := 2.0;
begin
   C := A + B;
   D := A * C; --  Compiler will complain here.
end Disallow;
Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
2

You can override them to be abstract, but I think this is the wrong approach. Rather than trying to get rid of operations, it's usually better to declare the type to only have the operations needed:

type Example is private;

function "+" (Left : Example; Right : Example) return Example;
-- "*" not defined

The problem with this for types that are otherwise numeric is that you can't use numeric literals with the type. This can be somewhat alleviated by things like

function "+" (Right : Integer) return Example;

so that you write +42 rather than 42, which isn't too bad, though it's still awkward in the presence of a binary operator: X / (+42).

Jeffrey R. Carter
  • 3,033
  • 9
  • 10
  • Actually did this already. Unless you hand write assembly for the operators, an additional copy occurs even at -O2 for GNAT, which causes substantial slowdowns. – Patrick Kelly Nov 28 '17 at 14:32
  • Funny, I just tried it, and using a private type with a full type of `type Number is new Integer;` and `function "+" (Left : Number; Right : Number) return Number is (Number (Integer (Left) + Integer (Right) ) );` turned out to be about a factor of 2.7 faster than using Integer directly. – Jeffrey R. Carter Nov 28 '17 at 18:34
  • Which compiler version and which platform? – Patrick Kelly Nov 29 '17 at 14:46
  • GNAT 7.2.0 on Xubuntu 17.10. Intel Core i7-6700HQ. I don't understand why there would be a difference; with the type conversions optimized away and the function call inlined, I'd think they would be the same. – Jeffrey R. Carter Nov 30 '17 at 11:37
  • 1
    With further experimentation, it appears that what I was timing had been optimized away. With modifications to prevent that, timings seem about the same. 1,000 calls to "+" gave similar timings, with the private type consistently a little faster; 1,000,000 calls had the private type about half the time; and 1,000,000,000 calls were again about the same, with the private type about 10% longer. While I don't understand the differences, this is more what I expected. – Jeffrey R. Carter Nov 30 '17 at 12:08
-1

You must define an overloaded function which produces a result of the type you want. While it is sometimes desirable to disable an intrinsic operator definition, it is not necessary to do so to get the operator definition you want.

type Example_Type is new float;
type Other_Type is new float;

function "*" (Left, Right : Example_Type) return Other_Type;

Ada can resolve the overloading based upon the return type of the function.

Jim Rogers
  • 4,822
  • 1
  • 11
  • 24
  • Well I'm perfectly aware of how to define new operators. That's not the issue at all. Other than the arguably unique naming convention, there's no difference to defining any other function. – Patrick Kelly Nov 26 '17 at 18:05
  • 2
    This doesn't hide the intrinsic one, precisely because operators overload on return type as well as arguments. –  Nov 27 '17 at 07:39