- TRecordEqualityComparer<T: record> = class(TEqualityComparer<T>)
- EqualityComparer_Vtable: array[0..4] of Pointer;
- EqualityComparer_Instance: Pointer;
- Equals: function(const left, right: T): Boolean;
- GetHashCode: function(Self: Pointer): Integer;
- class function EqualsProxy(inst: Pointer; const left, right: T): Boolean; static;
- class function GetHashCodeProxy(inst: Pointer; const value: T): Integer; static;
+ PComparerData = ^TComparerData;
+ TComparerData = record // same layout as Generics.Defaults.TSimpleInstance
+ Vtable: TArray<Pointer>;
+ constructor Create(typeSize: Integer; typeInfo: Pointer;
+ const defaultComparer: IInterface;
+ equalsAddr, getHashCodeAddr: Pointer);
+ TRecordEqualityComparer<T{: record}> = class
+ strict private class var
+ Instance: TComparerData;
+ class function Equals(inst: PComparerData; const left, right: T): Boolean; reintroduce; static;
+ class function GetHashCode(inst: PComparerData; const value: T): Integer; reintroduce; static;
class constructor Create;
class function Default: IEqualityComparer<T>; static;
-function NopAddref(inst: Pointer): Integer; stdcall;
-function NopRelease(inst: Pointer): Integer; stdcall;
-function NopQueryInterface(inst: Pointer; const IID: TGUID; out Obj): HResult; stdcall;
-function NopAddref(inst: Pointer): Integer; stdcall;
+function NopQueryInterface(inst: Pointer; const IID: TGUID; out Obj): HResult; stdcall;
+ Result := E_NOINTERFACE;
-function NopRelease(inst: Pointer): Integer; stdcall;
+function NopAddRef(inst: Pointer): Integer; stdcall;
-function NopQueryInterface(inst: Pointer; const IID: TGUID; out Obj): HResult; stdcall;
+function NopRelease(inst: Pointer): Integer; stdcall;
- Result := E_NOINTERFACE;
-{ TRecordEqualityComparer<T> }
-class constructor TRecordEqualityComparer<T>.Create;
+constructor TComparerData.Create(typeSize: Integer; typeInfo: Pointer;
+ const defaultComparer: IInterface; equalsAddr, getHashCodeAddr: Pointer);
+ TVtable = array[0..4] of Pointer;
params: TArray<TRttiParameter>;
- EqualityComparer_Vtable[0] := @NopQueryInterface;
- EqualityComparer_Vtable[1] := @NopAddref;
- EqualityComparer_Vtable[2] := @NopRelease;
- EqualityComparer_Vtable[3] := @TRecordEqualityComparer<T>.EqualsProxy;
- EqualityComparer_Vtable[4] := @TRecordEqualityComparer<T>.GetHashCodeProxy;
- EqualityComparer_Instance := @EqualityComparer_Vtable;
+ Vtable[0] := @NopQueryInterface;
+ Vtable[1] := @NopAddRef;
+ Vtable[2] := @NopRelease;
+ Default := defaultComparer;
// TODO: possibly use low level RTTI to be quicker and not have dependency on Rtti.pas
- for method in ctx.GetType(TypeInfo(T)).GetMethods do
+ for method in ctx.GetType(typeInfo).GetMethods do
if Assigned(GetHashCode) and Assigned(Equals) then
returnType := method.ReturnType.Handle;
isStatic := method.IsStatic;
- if (returnType = TypeInfo(Integer)) and not isStatic
+ if (returnType = System.TypeInfo(Integer)) and not isStatic
and SameText(method.Name, 'GetHashCode') then
params := method.GetParameters;
- if (returnType = TypeInfo(Boolean)) and isStatic
+ if (returnType = System.TypeInfo(Boolean)) and isStatic
and SameText(method.Name, '&op_Equality') then
params := method.GetParameters;
- and (params[0].ParamType.Handle = TypeInfo(T))
+ and (params[0].ParamType.Handle = typeInfo)
and (pfConst in params[0].Flags)
- and (params[1].ParamType.Handle = TypeInfo(T))
+ and (params[1].ParamType.Handle = typeInfo)
and (pfConst in params[0].Flags) then
Equals := method.CodeAddress;
+ if Assigned(Equals) then
+ Vtable[3] := equalsAddr
+ Vtable[3] := PPVTable(Default)^^[3];
+ if Assigned(GetHashCode) then
+ Vtable[4] := getHashCodeAddr
+ Vtable[4] := PPVTable(Default)^^[4];
+{ TRecordEqualityComparer<T> }
+class constructor TRecordEqualityComparer<T>.Create;
+ Instance := TComparerData.Create(SizeOf(T), TypeInfo(T),
+ TEqualityComparer<T>.Default, @Equals, @GetHashCode);
class function TRecordEqualityComparer<T>.Default: IEqualityComparer<T>;
Assert(GetTypeKind(T) = tkRecord);
- Assert(Assigned(Equals));
- Assert(Assigned(GetHashCode));
IInterface(Result) := nil;
- Pointer(Result) := @EqualityComparer_Instance;
+ Pointer(Result) := @Instance;
-class function TRecordEqualityComparer<T>.EqualsProxy(inst: Pointer; const left,
+class function TRecordEqualityComparer<T>.Equals(inst: PComparerData;
+ const left, right: T): Boolean;
+ TEquals = function(const left, right: T): Boolean;
- Result := Equals(left, right);
+ Result := TEquals(inst.Equals)(left, right);
-class function TRecordEqualityComparer<T>.GetHashCodeProxy(inst: Pointer;
- const value: T): Integer;
+class function TRecordEqualityComparer<T>.GetHashCode(
+ inst: PComparerData; const value: T): Integer;
+ TGetHashCode = function(Self: Pointer): Integer;
- Result := GetHashCode(@value);
+ Result := TGetHashCode(inst.GetHashCode)(@value);