Skip to content

Commit

Permalink
Fixed issue VSoftTechnologies#125 Access violation in TValueHelper.Co…
Browse files Browse the repository at this point in the history
…mpareValue for tkRecord

Added ability to register custom comparison routines which cann be used for all types but primarily designed to be used with records
  • Loading branch information
aly-sas committed Apr 10, 2020
1 parent 6a49b49 commit a08120f
Showing 1 changed file with 45 additions and 1 deletion.
46 changes: 45 additions & 1 deletion Source/Delphi.Mocks.Helpers.pas
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,20 @@
interface

uses
Rtti;
System.Generics.Collections,
System.Rtti;

type
//Allow custom comparisons
TCustomValueComparer = reference to function(const a, b: TValue): Integer;
TCustomValueComparerStore = record
private
class var CustomComparers: TDictionary<Pointer, TCustomValueComparer>;
public
class procedure RegisterCustomComparer<T>(const AComparer: TCustomValueComparer); static;
class procedure UnRegisterCustomComparer<T>; static;
end;

//TValue really needs to have an Equals operator overload!
TValueHelper = record helper for TValue
private
Expand Down Expand Up @@ -64,6 +75,7 @@ TValueHelper = record helper for TValue
function IsWord: Boolean;
function IsGuid: Boolean;
function IsInterface : Boolean;
function IsRecord: Boolean;
function AsDouble: Double;
function AsFloat: Extended;
function AsSingle: Single;
Expand Down Expand Up @@ -101,11 +113,14 @@ function CompareValue(const Left, Right: TValue): Integer;
EmptyResults: array[Boolean, Boolean] of Integer = ((0, -1), (1, 0));
var
leftIsEmpty, rightIsEmpty: Boolean;
CustomComparer: TCustomValueComparer;
begin
leftIsEmpty := left.IsEmpty;
rightIsEmpty := right.IsEmpty;
if leftIsEmpty or rightIsEmpty then
Result := EmptyResults[leftIsEmpty, rightIsEmpty]
else if (Left.TypeInfo = Right.TypeInfo) and TCustomValueComparerStore.CustomComparers.TryGetValue(Left.TypeInfo, CustomComparer) then
Result := CustomComparer(Left, Right)
else if left.IsOrdinal and right.IsOrdinal then
Result := Math.CompareValue(left.AsOrdinal, right.AsOrdinal)
else if left.IsFloat and right.IsFloat then
Expand All @@ -116,6 +131,8 @@ function CompareValue(const Left, Right: TValue): Integer;
Result := NativeInt(left.AsObject) - NativeInt(right.AsObject) // TODO: instance comparer
else if Left.IsInterface and Right.IsInterface then
Result := NativeInt(left.AsInterface) - NativeInt(right.AsInterface) // TODO: instance comparer
else if Left.IsRecord and Right.IsRecord then
raise Exception.Create('Use Delphi.Mocks.Helpers.TCustomValueComparerStore.RegisterCustomComparer<T> to add a method to compare records.')
else if left.IsVariant and right.IsVariant then
begin
case VarCompareValue(left.AsVariant, right.AsVariant) of
Expand Down Expand Up @@ -236,6 +253,11 @@ function TValueHelper.IsPointer: Boolean;
Result := Kind = tkPointer;
end;

function TValueHelper.IsRecord: Boolean;
begin
Result := Kind = tkRecord;
end;

function TValueHelper.IsShortInt: Boolean;
begin
Result := TypeInfo = System.TypeInfo(ShortInt);
Expand Down Expand Up @@ -307,4 +329,26 @@ function TRttiTypeHelper.TryGetMethod(const AName: string; out AMethod: TRttiMet
Result := Assigned(AMethod);
end;



{ TCustomValueComparerStore }

class procedure TCustomValueComparerStore.RegisterCustomComparer<T>(const AComparer: TCustomValueComparer);
begin
CustomComparers.Add(System.TypeInfo(T), AComparer);
end;

class procedure TCustomValueComparerStore.UnRegisterCustomComparer<T>;
begin
CustomComparers.Remove(System.TypeInfo(T));
end;



initialization
TCustomValueComparerStore.CustomComparers := TDictionary<Pointer, TCustomValueComparer>.Create;

finalization
TCustomValueComparerStore.CustomComparers.Free;

end.

0 comments on commit a08120f

Please sign in to comment.