4

私は次のようなデータ構造を持つコードで作業しています:

type
  TData1 = record
    IntField: Integer;
    StrField: string;
  end;

  TData2 = record
    DateField: TDateTime;
    StrField: string;
  end;

var
  AData1 = array of ^TData1;
  AData2 = array of ^TData2;

次のように、その配列の 1 つに要素を追加する必要がある場合があります。

L := Length(AData1);
SetLength(AData1, L + 1);
New(AData1[L]);

配列サイズを増やして、あらゆる種類のポインターで機能する新しいアイテムにメモリを割り当てるという仕事をするプロシージャを作成するにはどうすればよいでしょうか? また、既存のコードが壊れないように、レコード (TData1、TData2) と配列 (AData1、AData2) の定義を変更せずに実行する必要があります。

Ps: 私はポインターやその種のプログラミングについてあまり知識がありません。確かにオブジェクトと動的リンク リストを使用しますが、この場合はレガシー コードであり、少なくとも今のところは変更できません。

4

1 に答える 1

2

アップデート:

これは完全な答えではありません。Davidが述べたように使用TDynArrayすると、問題が解決する場合があります。

RTTIを使用して、別のソリューションを作成しました。これは一般的なソリューションであり、機能や機能を簡単に追加できます。型宣言は保持されます。レコードの追加/削除の例が含まれています。

レコードTDynPtArrayは、レコードへのポインターの動的配列を処理します。Initこれは、次の呼び出しで初期化されます。

DPA.Init(TypeInfo(TData1), AData1);
Data1 := DPA.Add;  // Adds a record with default values and
                   // returns a pointer to the record
DPA.Remove; // Finalizes/deallocates the last record and 
            // shrinks the dynamic array

-

uses
  Windows,System.SysUtils,System.TypInfo;

Type
  TPtArray = array of Pointer;
  PPtArray = ^TPtArray;
  TDynPtArray = record
  private
    FDynArray: PPtArray;
    FTypeInfo: PTypeInfo;
    FTypeData: PTypeData;
  public
    constructor Init( T: Pointer; var dynArray);
    function Add : Pointer;
    procedure Remove;
    procedure Clear;
  end;

constructor TDynPtArray.Init(T: Pointer; var dynArray);
begin
  FTypeInfo := T;
  if  (FTypeInfo^.Kind <> tkRecord) then
    raise Exception.CreateFmt('%s is not a record',[FTypeInfo^.Name]);
  FTypeData := GetTypeData( FTypeInfo);
  FDynArray := @dynArray;
end;

function TDynPtArray.Add: Pointer;
var
  L: integer;
begin
  L := Length(FDynArray^);
  SetLength(FDynArray^,L+1);
  GetMem( FDynArray^[L], FTypeData^.elSize);
  ZeroMemory( FDynArray^[L], FTypeData^.elSize);
  Result := FDynArray^[L];
end;

procedure RecordClear(var Dest; TypeInfo: pointer);
asm
{$ifdef CPUX64}
  .NOFRAME
{$endif}
  jmp System.@FinalizeRecord
end;

procedure TDynPtArray.Remove;
var
 L: integer;
begin
  L := Length(FDynArray^);
  if (L = 0) then
    exit;
  RecordClear( FDynArray^[L-1]^,FTypeInfo); // Finalize record
  FreeMem( FDynArray^[L-1], FTypeData^.elSize);
  SetLength(FDynArray^,L-1);
end;

procedure TDynPtArray.Clear;
begin
  while (Length(FDynArray^) <> 0) do
    Self.Remove;
end;

そして少しテスト:

type
  PData1 = ^TData1;
  TData1 = record
    IntField: Integer;
    StrField: string;
  end;
  TData1Arr = array of PData1;

var
  AData1: TData1Arr;
  Data1: PData1;
  DPA: TDynPtArray;
begin
  DPA.Init(TypeInfo(TData1), AData1);
  Data1:= DPA.Add;
  Data1^.StrField := '111';
  WriteLn(Data1^.IntField);
  WriteLn(Data1^.StrField);

  DPA.Clear;

  ReadLn;
end.
于 2012-08-03T18:51:12.287 に答える