2016-01-13 4 views
12

TQueueに配列を格納する際に問題が発生しました。どこが間違っているのか? コードはDelphi XE 5では正常に動作しますが、Delphi 10 Seattleでは正常に動作しません。TQueueに配列を格納できますか?

(これはバグですか、それがどのように動作するかをどうかを決定することはできません。手がかりをエンバカデロ検索を試みたが失敗しました。)

procedure TForm1.Button1Click(Sender: TObject); 
var 
    FData: TQueue<TBytes>; 
    FsData: TQueue<String>; 

    arr: TBytes; 

begin 

    FData := TQueue<TBytes>.Create; 
    FsData := TQueue<String>.Create; 
    try 
    setlength(arr, 3); 
    arr[0] := 1; 
    arr[1] := 2; 
    arr[2] := 3; 

    FData.Enqueue(arr); 
    Memo1.Lines.Add('Count, array:' + IntToStr(FData.Count)); // 0? 

    FsData.Enqueue('asada'); 
    Memo1.Lines.Add('Count, string:' + IntToStr(FsData.Count)); // 1 
    finally 
    FData.Free; 
    FsData.Free; 
    end; 
end; 
+1

他にも、互換性のない別のバイト配列型は必要ありません。 'TBytes'を使用してください。より一般的には、Byte以外の要素型には 'TArray 'を使用します。 –

+0

私は同意します。元の配列はTidBytes(Indy)です – Hans

+1

何がうまくいかないのですか? –

答えて

20

これはXE8で導入された欠陥です。ここに私が作ることができる最も簡単な再生があります。

{$APPTYPE CONSOLE} 

uses 
    System.Generics.Collections; 

var 
    Queue: TQueue<TArray<Byte>>; 

begin 
    Queue := TQueue<TArray<Byte>>.Create; 
    Queue.Enqueue(nil); 
    Writeln(Queue.Count); 
end. 

出力はXE7では1、XE8とシアトルでは0です。

これは既にEmbarcadero:RSP-13196に報告されています。


Enqueueの実装は次のようになりますTダイナミックアレイの場合

procedure TQueue<T>.Enqueue(const Value: T); 
begin 
    if IsManagedType(T) then 
    if (SizeOf(T) = SizeOf(Pointer)) and (GetTypeKind(T) <> tkRecord) then 
     FQueueHelper.InternalEnqueueMRef(Value, GetTypeKind(T)) 
    else 
     FQueueHelper.InternalEnqueueManaged(Value) 
    else 
    case SizeOf(T) of 
    1: FQueueHelper.InternalEnqueue1(Value); 
    2: FQueueHelper.InternalEnqueue2(Value); 
    4: FQueueHelper.InternalEnqueue4(Value); 
    8: FQueueHelper.InternalEnqueue8(Value); 
    else 
    FQueueHelper.InternalEnqueueN(Value); 
    end; 
end; 

FQueueHelper.InternalEnqueueMRef分岐が選択されます。

procedure TQueueHelper.InternalEnqueueMRef(const Value; Kind: TTypeKind); 
begin 
    case Kind of 
    TTypeKind.tkUString: InternalEnqueueString(Value); 
    TTypeKind.tkInterface: InternalEnqueueInterface(Value); 
{$IF not Defined(NEXTGEN)} 
    TTypeKind.tkLString: InternalEnqueueAnsiString(Value); 
    TTypeKind.tkWString: InternalEnqueueWideString(Value); 
{$ENDIF} 
{$IF Defined(AUTOREFCOUNT)} 
    TTypeKind.tkClass: InternalEnqueueObject(Value); 
{$ENDIF} 
    end; 
end; 

TTypeKind.tkDynArrayのエントリがないこと:これは、順番に次のようになります。これらの2つのメソッドはインライン展開されているため、インライナはそれをすべて圧縮して無駄にします。 Enqueueダイナミックアレイの場合は何も実行されません。そこタイプ特定の欠陥のために

procedure TQueue<T>.Enqueue(const Value: T); 
begin 
    if Count = Length(FItems) then 
    Grow; 
    FItems[FHead] := Value; 
    FHead := (FHead + 1) mod Length(FItems); 
    Inc(FCount); 
    Notify(Value, cnAdded); 
end; 

ない範囲:

戻るXE7の古き良き時代のコードはこのように見えました。


私は簡単な回避策はないと思います。 XE7 TQueueのコードをとり、XE8とSeattleの壊れた実装の代わりにそれを使用するのが最も適切な方法でしょう。記録のために、私はEmbarcaderoのジェネリックコレクションをあきらめ、自分のクラスを使用しました。ここ


バックストーリーがXE8で、エンバカデロがジェネリック医薬品のその実装の不足に対処することを決定したということです。ジェネリック型をインスタンス化すると、すべてのメソッドのコピーが作成されます。いくつかの方法では、異なるインスタンス化に対して同一のコードが生成される。

TGeneric<TFoo>.DoSomethingTGeneric<TBar>.DoSomethingは同じコードを持つことがよくあります。他の言語用の他のコンパイラ、C++テンプレート、.netジェネリックなどは、この複製を認識し、同じジェネリックメソッドを一緒にマージします。 Delphiコンパイラはそうではありません。最終結果は、厳密に必要な実行可能ファイルよりも大きい実行可能ファイルです。

XE8では、エンバカデロはこれについて、私が間違った方法でこれに取り組むことに決めました。コンパイラは、問題の根本原因を攻撃する代わりに、ジェネリックコレクションクラスの実装を変更することに決めました。 Generics.Collectionsのコードを見ると、XE8で完全に書き直されていることがわかります。以前はXE7以前のコードが読めるところでは、XE8から非常に複雑で不透明になっています。この決定には次のような結果がありました。

  1. 複合コードには多くのエラーが含まれていました。これらの多くは、XE8がリリースされた直後に発見され、修正されました。あなたは別の欠陥を見つけました。私たちが学んだことの1つは、Embarcaderoの内部テストスイートがコレクションクラスを十分に実行していないことです。彼らのテストが不十分であることは明らかに明らかです。
  2. コンパイラではなくライブラリを変更することにより、RTLクラスにパッチを適用しました。ジェネリックコードの膨大な問題は、サードパーティのクラスに残っています。 Embarcaderoがソースで問題を修正した場合、XE7からシンプルで正確なコレクションクラスコードを保持できるだけでなく、すべての3番目の汎用コードが恩恵を受けていました。
+1

それを選別していただきありがとうございます – Hans

関連する問題