如何使用Move复制类型是否安全? [英] How can I test if a type is safe to copy using Move

查看:131
本文介绍了如何使用Move复制类型是否安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个通用的矢量类型:

  type 
TBigVector< T:record> = class
private
FSize:Integer;
FEntries:TArray< T>;
函数GetEntry(Index:Integer):T;
程序SetEntry(Index:Integer; const Value:T);
过程SetSize(Value:Integer);
public
构造函数Create(ASize:Integer);
属性大小:整数读取FSize写SetSize;
属性Entry [Index:Integer]:T读取GetEntry写入SetEntry;默认;
程序零度;
函数ToArray:TArray< T>;
结束

然后我想推出这样的课程:

  TDoubleBigVector = class(TBigVector< Double>)
....
end;
TComplexBigVector = class(TBigVector< Complex>)
....
end;

我必须派生类,因为我不能在泛型类型中实现算术运算。这是因为通用约束不足以使我限制 T 来支持所需的算术运算。 Grr!



无论如何,足够了。我的问题涉及到 Zeroise ToArray 的实现。出于性能原因,我想使用原始内存操作。例如, Zeroise 可能是:

 程序TBigVector&T。零零度
begin
ZeroMemory(指针(FEntries),Size * SizeOf(T));
结束

现在,对于像 Double 和我的定制复杂类型。我知道他们没有管理,原始的内存副本没有创造出困难。我想做的是添加一个运行时检查,也许只有在我的调试版本中调用,强制执行 T 没有托管类型的约束。我该怎么做?

解决方案

我会这样做:

 程序SO21753006; 

{$ APPTYPE CONSOLE}

使用
TypInfo,
Windows,
SysUtils;

类型
TProblemRecord1 =记录
I:整数;
S:String;
结束

TProblemRecord2 = record
Obj:TObject;
结束

TGoodRecord = record
I:整数;
K:双;
S:数组[0..10]的Char;
结束


TBigVector< T:record> = class
private
FSize:Integer;
FEntries:TArray< T>;
函数GetEntry(Index:Integer):T;
程序SetEntry(Index:Integer; const Value:T);
过程SetSize(Value:Integer);
public
构造函数Create(ASize:Integer);
属性大小:整数读取FSize写SetSize;
属性Entry [Index:Integer]:T读取GetEntry写入SetEntry;默认;
程序零度;
函数ToArray:TArray< T>;
结束


函数RecordHasNoManagedTypes(Typ:PTypeInfo):Boolean;

var
TypeData:PTypeData;

begin
Assert(Assigned(Typ));
结果:= True;
//只检查我们是否有记录,否则我们有一个值类型
如果Typ.Kind = tkRecord然后
begin
TypeData:= GetTypeData(Typ);
结果:= TypeData.ManagedFldCount = 0;
结束
结束

{TBigVector< T> }

构造函数TBigVector< T> .Create(ASize:Integer);
begin
大小:= ASize;
结束

函数TBigVector> .GetEntry(Index:Integer):T;
begin
end;

程序TBigVector< T> .SetEntry(Index:Integer; const Value:T);
begin
end;

程序TBigVector< T> .SetSize(Value:Integer);
begin
SetLength(FEntries,Value);
结束

函数TBigVector&T; .ToArray:TArray< T>;
begin
end;

程序TBigVector< T> .Zeroise;
begin
Assert(RecordHasNoManagedTypes(TypeInfo(T)),'T不能有托管类型!
ZeroMemory(指针(FEntries),Size * SizeOf(T));
结束

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>。创建(10);
Rec2.Zeroise;
try
Writeln('Problemrecord1 type');
Rec3:= TBigVector< TProblemRecord1>。创建(10);
Rec3.Zeroise;
除了
在E:Exception do
Writeln(E.ClassName,':',E.Message);
结束
try
Writeln('Problemrecord2 type');
Rec4:= TBigVector< TProblemRecord2>。创建(10);
Rec4.Zeroise;
除了
在E:Exception do
Writeln(E.ClassName,':',E.Message);
结束
除了
在E:Exception do
Writeln(E.ClassName,':',E.Message);
结束
Readln;
结束。对于非ARC平台,

TObject 管理这样使用 ManagedFldCount 在此处有效。



更新

$如David所指出,在更新的Delphi版本(Read:from D2010)中,您可以使用 Rtti.IsManaged 函数。



代码如下所示:

 程序TBigVector< T> .Zeroise; 
begin
Assert(不是RTTI.IsManaged(TypeInfo(T)),'T不能有托管类型!
ZeroMemory(指针(FEntries),Size * SizeOf(T));
结束


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?

解决方案

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;

这篇关于如何使用Move复制类型是否安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆