How can I test if a type is safe to copy using Move
delphi
Solution
I would do it like this:
program SO21753006;
{$APPTYPE CONSOLE}
uses
TypInfo,
Windows,
SysUtils;
type
TProblemRecord1 = record
I : Integer;
S : String;
end;
TProblemRecord2 = record
Obj : TObject;
end;
TGoodRecord = record
I : Integer;
K : Double;
S : Array[0..10] of Char;
end;
TBigVector<T: record> = class
private
FSize: Integer;
FEntries: TArray<T>;
function GetEntry(Index: Integer): T;
procedure SetEntry(Index: Integer; const Value: T);
procedure SetSize(Value: Integer);
public
constructor Create(ASize: Integer);
property Size: Integer read FSize write SetSize;
property Entry[Index: Integer]: T read GetEntry write SetEntry; default;
procedure Zeroise;
function ToArray: TArray<T>;
end;
function RecordHasNoManagedTypes(Typ : PTypeInfo) : Boolean;
var
TypeData : PTypeData;
begin
Assert(Assigned(Typ));
Result := True;
// only check if we have a record, or else we have a value type
if Typ.Kind = tkRecord then
begin
TypeData := GetTypeData(Typ);
Result := TypeData.ManagedFldCount = 0;
end;
end;
{ TBigVector<T> }
constructor TBigVector<T>.Create(ASize: Integer);
begin
Size := ASize;
end;
function TBigVector<T>.GetEntry(Index: Integer): T;
begin
end;
procedure TBigVector<T>.SetEntry(Index: Integer; const Value: T);
begin
end;
procedure TBigVector<T>.SetSize(Value: Integer);
begin
SetLength(FEntries, Value);
end;
function TBigVector<T>.ToArray: TArray<T>;
begin
end;
procedure TBigVector<T>.Zeroise;
begin
Assert(RecordHasNoManagedTypes(TypeInfo(T)), 'T must not have managed types!');
ZeroMemory(Pointer(FEntries), Size*SizeOf(T));
end;
var
Rec1 : TBigVector<Double>;
Rec2 : TBigVector<TGoodRecord>;
Rec3 : TBigVector<TProblemRecord1>;
Rec4 : TBigVector<TProblemRecord2>;
begin
try
Writeln('Double type');
Rec1 := TBigVector<Double>.Create(1);
Rec1.Zeroise;
Writeln('GoodRecord type');
Rec2 := TBigVector<TGoodRecord>.Create(10);
Rec2.Zeroise;
try
Writeln('Problemrecord1 type');
Rec3 := TBigVector<TProblemRecord1>.Create(10);
Rec3.Zeroise;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
try
Writeln('Problemrecord2 type');
Rec4 := TBigVector<TProblemRecord2>.Create(10);
Rec4.Zeroise;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
`TObject` for non-ARC platforms is not managed so the use `ManagedFldCount` is valid here.
UPDATE
As David pointed out, in more recent Delphi versions (Read: from D2010) you can use the Rtti.IsManaged function.
the code would look like this:
procedure TBigVector<T>.Zeroise;
begin
Assert(not RTTI.IsManaged(TypeInfo(T)), 'T must not have managed types!');
ZeroMemory(Pointer(FEntries), Size*SizeOf(T));
end;
UPDATE 2 From XE7 onwards you can use the intrinsic function `System.IsManagedType(T)`. This will resolve at compile-time, incurring zero runtime overhead.
if IsManagedType(T) then Assert(false, 'T most not be a managed type');
Don't do `Assert(not(IsManagedType(T))` because the compiler will not be able to remove the Assert, but it will eliminate the `if` if it does not apply.
Problem
I'm writing a generic vector type: ``` type TBigVector<T: record> = class private FSize: Integer; FEntries: TArray<T>; function GetEntry(Index: Integer): T; procedure SetEntry(Index: Integer; const Value: T); procedure SetSize(Value: Integer); public constructor Create(ASize: Integer); property Size: Integer read FSize write SetSize; property Entry[Index: Integer]: T read GetEntry write SetEntry; default; procedure Zeroise; function ToArray: TArray<T>; end; ``` I then want to derive classes like this: ``` TDoubleBigVector = class(TBigVector<Double>) .... end; TComplexBigVector = class(TBigVector<Complex>) .... end; ``` I'm having to derive classes because I cannot implement arithmetic operations in the generic type. That is because generic constraints are not rich enough for me to constrain `T` to support the required arithmetic operations. Grr! Anyway, enough of that. My question concerns the implementation of `Zeroise` and `ToArray`. For performance reasons, I want to use raw memory operations. For instance, `Zeroise` might be: ``` procedure TBigVector<T>.Zeroise; begin ZeroMemory(Pointer(FEntries), Size*SizeOf(T)); end; ``` Now, I'm fine with doing that for types like `Double` and my bespoke `Complex` type. I know that they are not managed and a raw memory copy creates no difficulty. What I would like to do is add a runtime check, perhaps only called in my debug builds, that enforces the constraint that `T` has no managed types. How can I do that?