FUNCTION "=" (lString1, lString2 : IN lString) RETURN boolean IS
IF lString1 = NULL AND lString2 = NULL THEN
RETURN true;
ELSIF lString1 = NULL OR lString2 = NULL THEN
RETURN false;
END IF;
I'm trying to overload the equality operator in Ada. Each time I use the operator '=' within the function it causes a recursion which leads to a stack overflow, rather than use the ada defined operator which I need. Is there a way to d开发者_如何学Cifferentiate it from my overloaded operator?
By introducing a non-overloaded utility function to do the access type comparisons, the OP's function definition, with the needed syntax fixes and modified to use the utility function, can be made to work.
I'm still puzzled, though, as to why invoking "=" as Standard."=" is rejected by the compiler (GNAT) for specifying "incompatible arguments".
with Text_IO; use Text_IO;
procedure non_recursive_equals is
type Lstring is access String;
-- Be aware, the ordering of the functions here is important!
function Is_Equal(Lstring1, Lstring2 : in Lstring) return Boolean is
begin
return Lstring1 = Lstring2;
end Is_Equal;
function "=" (lString1, lString2 : in Lstring) return Boolean is
begin
if Is_Equal(LString1, null) and Is_Equal(LString2, null) then
return True;
elsif Is_Equal(LString1, null) or Is_Equal(LString2, null) then
return False;
end if;
return False;
end "=";
L1, L2 : Lstring := null;
begin
Put_Line("L1 and L2 null: " & Boolean'Image(L1 = L2));
L2 := new String(1..10);
Put_Line("L2 not null : " & Boolean'Image(L1 = L2));
end non_recursive_equals;
Edit:
Here's another way, using a renames clause:
with Text_IO; use Text_IO;
procedure non_recursive_equals is
type Lstring is access String;
function Is_Equal (lString1, lString2 : in Lstring) return Boolean is
begin
if lString1 = null and lString2 = null then
return True;
elsif lString1 = null or lString2 = null then
return False;
end if;
return False;
end Is_Equal;
function "=" (Lstring1, Lstring2 : in Lstring) return Boolean renames
Is_Equal;
L1, L2 : Lstring := null;
begin
Put_Line ("L1 and L2 null: " & Boolean'Image (L1 = L2));
L2 := new String (1 .. 10);
Put_Line ("L2 not null : " & Boolean'Image (L1 = L2));
end non_recursive_equals;
Here's yet another way, using only Ada83... and a horrid example/abuse of exceptions:
Type LString is Access String;
Function "=" (Left, Right: IN LString) Return Boolean is
Subtype Constrained_LString is Not Null LString;
Function Is_Equal( Left : LString; Right : String ) Return Boolean is
begin
Return Right = Left.All;
exception
When CONSTRAINT_ERROR => Return False;
end Is_Equal;
Begin
Return Is_Equal(Left, Right.All);
Exception
When CONSTRAINT_ERROR =>
begin
Return Is_Equal(Right,Left.All);
Exception
When CONSTRAINT_ERROR => Return True;
end;
End "=";
What happens is if it is called and Right = Null the attempt to de-reference it causes an exception; in this case we try to de-reference Left and if that too fails then both must be Null. In the case where only one fails the equality must be false and in the case where both parameters can be de-referenced the result is the test for equality on those strings.
I was able to reproduce the same behavior with similar code. I took the liberty of assuming that lString
was some sort of string access
type
I believe the recursion is being caused by the fact that your new =
function masks the natively provided one. Since they share both the same name, parameters, and return value, there is no straightforward way of distinguishing between the two.
An inelegant way around this would be to avoid overloading entirely and to define a new function with the same behavior as your overloaded function, with a different name such as Is_Equal
.
I'm not sure why "=" is being used recursively; possibly, there is an unfortunate use
clause present. The example below overloads "="
and produces the following output. The overloaded function implicitly invokes Standard."="
for the comparison. Note you can specify renames
to simplify package names, and you can use type
to expose just the operators applicable to a type.
Addendum: I've added an alternate way to invoke Standard."="
in a comment below.
Console:
******************** ******************** TRUE TRUE
Code:
with Ada.Strings.Bounded;
with Ada.Strings.Unbounded;
with Ada.Text_IO;
procedure UseType is
package String20 is new Ada.Strings.Bounded.Generic_Bounded_Length(20);
use type String20.Bounded_String;
package StringN renames Ada.Strings.Unbounded;
use type StringN.Unbounded_String;
function "=" (Left : String20.Bounded_String;
Right : StringN.Unbounded_String) return Boolean is
begin
return String20.To_String(Left) = StringN.To_String(Right);
-- return Standard."="(String20.To_String(Left), StringN.To_String(Right));
end "=";
SB : constant String20.Bounded_String := 20 * '*';
SN : constant StringN.Unbounded_String := 20 * '*';
begin
Ada.Text_IO.Put_Line(String20.To_String(SB));
Ada.Text_IO.Put_Line(StringN.To_String(SN));
Ada.Text_IO.Put_Line(Boolean'Image(SB = SN)); -- infix operator
Ada.Text_IO.Put_Line(Boolean'Image("="(SB, SN))); -- named operator
end UseType;
In some cases, perhaps most or all cases, you shouldn't embed the logic that "two access-to-strings are equal if they are both null" into an abstraction (such as a package).
Consider the example where a bank transaction processing program reads the payee's name from two files, so ending up with data in Payee_Rec
and Trans_Rec
, which both have a Name
field that is an access-to-string.
In this case, null
means that the data (the payee's name) is not recorded, for some reason, in the record.
Somewhere we will check that the payee's name in both records is the same, and reject the transaction if they are not.
If the Names are of type lstring
and the test we use looks like this:
if Payee_Rec.Name /= Trans_Rec.Name then
raise Validation_Error;
end if;
then if the two names are both null, this check will pass.
But it should not! If we don't even know what the payee's name is (in both records) the transaction should fail.
So my answer, while it may seem disappointing and even off topic, is "Don't do that."
I suggest that a well-written program would use Ada.String.Unbounded.Unbounded_String
as the type (for both Name
components, in the example above). If the source of the data (e.g. a database) can have null strings (for the names), then have a separate Boolean
flag to indicate that, e.g. Name_Unknown
.
The above example check could then be written in a very clear way:
if Payee_Rec.Name_Unknown
or Trans_Rec.Name_Unknown
or Payee_Rec.Name /= Trans_Rec.Name then
raise Validation_Error;
end if;
Note that you'll need the requisite use type ...
to see the Unbounded_String
equality (and inequality) operator.
You'd probably also want to validate both names that they are not empty strings or other nonsense, and you might want to do something more sophisticated than just raising an exception if a check fails.
You may want to use the NOT EQUAL operator negated, maybe? If you have no use for that operator, it is.
Which probably you shouldn't really need, because it is equivalent to not( X = Y )
Working the boolean algebra it should probably be something like:
FUNCTION "=" (lString1, lString2 : IN lString) RETURN boolean IS
IF not (lString1 /= NULL OR lString2 /= NULL) THEN
RETURN true;
ELSIF not(lString1 /= NULL AND lString2 /= NULL) THEN
RETURN false;
END IF;
PS: Test it, I didn't
精彩评论