Implementing Comparison Operations

From RAD Studio
Jump to: navigation, search

Go Up to Creating a Class to Enable the Custom Variant Type


There are two ways to enable a custom variant type to support comparison operators (=, <>, <, <=, >, >=). You can either override the Compare method, or you can override the CompareOp method.

The Compare method is easier if your custom variant type supports the full range of comparison operators. Compare takes three parameters: the left-hand operand, the right-hand operand, and a var Parameter that returns the relationship between the two.

For example, the TConvertVariantType object in the VarConv unit implements the following Compare method:

Delphi:

 procedure TConvertVariantType.Compare(const Left, Right: TVarData;
 var Relationship: TVarCompareResult);
 const
 CRelationshipToRelationship: array [TValueRelationship] of TVarCompareResult =
 (crLessThan, crEqual, crGreaterThan);
 var
 LValue: Double;
 LType: TConvType;
 LRelationship: TValueRelationship;
 begin
 // supports...
 //   convvar cmp number
 //     Compare the value of convvar and the given number
   //   convvar1 cmp convvar2
 //     Compare after converting convvar2 to convvar1's unit type
   //  The right can also be a string.  If the string has unit info then it is
 //    treated like a varConvert else it is treated as a double
 LRelationship := EqualsValue;
 case Right.VType of
     varString:
       if TryStrToConvUnit(Variant(Right), LValue, LType) then
         if LType = CIllegalConvType then
           LRelationship := CompareValue(TConvertVarData(Left).VValue, LValue)
         else
           LRelationship := ConvUnitCompareValue(TConvertVarData(Left).VValue,
                                              TConvertVarData(Left).VConvType, LValue, LType)
         else
           RaiseCastError;
     varDouble:
       LRelationship := CompareValue(TConvertVarData(Left).VValue, TVarData(Right).VDouble);
     else
       if Left.VType = VarType then
         LRelationship := ConvUnitCompareValue(TConvertVarData(Left).VValue,
                            TConvertVarData(Left).VConvType, TConvertVarData(Right).VValue,
                            TConvertVarData(Right).VConvType)
       else
         RaiseInvalidOp;
   end;
   Relationship := CRelationshipToRelationship[LRelationship];
 end;

If the custom type does not support the concept of "greater than" or "less than," only "equal" or "not equal," it is difficult to implement the Compare method, because Compare must return crLessThan, crEqual, or crGreaterThan. When the only valid response is "not equal," it is impossible to know whether to return crLessThan or crGreaterThan.

Thus, for types that do not support the concept of ordering, you can override the CompareOp method instead.

CompareOp has three parameters: the value of the left-hand operand, the value of the right-hand operand, and the comparison operator. Implement this method to perform the operation and return a boolean that indicates whether the comparison is True. You can then call the RaiseInvalidOp method when the comparison makes no sense.

For example, the following CompareOp method comes from the TComplexVariantType object in the System.VarCmplx unit. It supports only a test of equality or inequality:

Delphi:

 function TComplexVariantType.CompareOp(const Left, Right: TVarData;
 const Operator: Integer): Boolean;
 begin
   Result := False;
 if (Left.VType = VarType) and (Right.VType = VarType) then
     case Operator of
       opCmpEQ:
         Result := TComplexVarData(Left).VComplex.Equal(TComplexVarData(Right).VComplex);
       opCmpNE:
         Result := not TComplexVarData(Left).VComplex.Equal(TComplexVarData(Right).VComplex);
     else
       RaiseInvalidOp;
     end
 else
   RaiseInvalidOp;
 end;

Note that the types of operands that both these implementations support are very limited. As with Implementing Binary Operations, you can use the RightPromotion and LeftPromotion methods to limit the cases you must consider by forcing a cast before Compare or CompareOp is called.

See Also