1

インクルード ファイルを使用して Delphi でセミジェネリック コンテナを作成するための古いハックがあります。

http://www.delphikingdom.com/asp/viewitem.asp?catalogid=453&mode=printを参照し、3D リストから始めてアイデアを把握してください。

interfaceただし、ユニットとセクションに 2 つの相互に依存する INC ファイルがあるとimplementation、問題が発生します。XE2 は、これらのインクルード ファイルを独立したユニットとしてコンパイルし、実装がインターフェイスで宣言された関数を見つけることができないようです。毎回発生するわけではありませんが、条件を特定できなかったため、回避策を講じることができませんでした。

可能な限り変更を加えずに Delphi ジェネリック ユニットでこれを再定式化しようとすると (私は巨大なレガシー プロジェクトを XE2 に移動する必要があり、「作業」は最初に、最適化とリファクタリングを後で行う必要があります)、次のピットに行き詰まりました:

  TemplateList<_DATA_TYPE_> = class  
  public
    const MaxListSize = Maxint div (sizeof(Integer)*sizeof(_DATA_TYPE_));    
    type
      TIntList = array[0..MaxListSize - 1] of _DATA_TYPE_;   
      PIntList = ^TIntList;                                  
  private
    FList: PIntList;
    FCount: Integer;

これにより、Low-boundTIntListが High-bound よりも高いというエラーが発生します。const MaxListSizeこれは、それがゼロに評価されることを意味すると思いTIntTypeますが、型を実際にインスタンス化するときではなく、すぐに評価されようとします。

XE3 または XE4 でこれが修正されたのだろうか。そして、大幅な再作業なしで XE2 でこれをコンパイルする方法があれば

PS。配列 0..0 を作成し、境界チェックを抑制するのが通常の解決策ですが、多くの壊れやすいチェックされていないコードが作成されます。多分私は実際の代わりに使用TListすることになるでしょう...TList<integer\>

PPS。面白いことに、内部型をコピペで再構築

TIntList = array[0..Maxint div (sizeof(Integer)*sizeof(_DATA_TYPE_)) - 1] of _DATA_TYPE_;

エラーを「const式が必要です」に変更します。

したがって、同じ式は、コンパイラのあるブランチでは const-enough と見なされ、別のブランチでは non-const と見なされます...それ自体が不整合バグを構成するのではないかと思います。

4

1 に答える 1

3

sizeof(_DATA_TYPE_)コンパイラの問題は、コンパイルの一般的な段階では不明であるように思われます。そのため、コンパイラは のプレースホルダー値を使用しているように見えます0。ジェネリック型をインスタンス化するまでにsizeof(_DATA_TYPE_)、真の値に置き換えられます。しかし、手遅れです。配列型の境界チェックは、コンパイルの一般的な段階で実行されるsizeof(_DATA_TYPE_)ため0、コンパイラはギャグします。

これが事実であることは、次のコードで確認できます。

type
  TemplateList<_DATA_TYPE_> = class
  public
    const
      SizeOfDataType = sizeof(_DATA_TYPE_);
      MaxListSize = Maxint div (sizeof(Integer)*SizeOfDataType);
  end;

これにより、次のコンパイラ エラーが生成されます。

[dcc32 エラー]: E2098 ゼロ除算

ただし、このバリアントを試すと、次のようになります。

{$APPTYPE CONSOLE}

type
  TemplateList<_DATA_TYPE_> = class
  public
    const
      SizeOfDataType = sizeof(_DATA_TYPE_);
  end;

begin
  Writeln(TemplateList<Integer>.SizeOfDataType);
  Writeln(TemplateList<Double>.SizeOfDataType);
  Readln;
end.

出力は次のとおりです。

4
8

これは、定数宣言に配列型境界チェック用のプレースホルダー値があることを示していますが、ジェネリックがインスタンス化されると true 値になります。したがって、コンパイラがインスタンス化まで配列型の境界チェックを延期した場合、コンパイラの警告に関してはすべて問題ありません。しかし、その場合でも、コードは期待どおりに機能しません。このプログラム:

{$APPTYPE CONSOLE}

type
  TemplateList<_DATA_TYPE_> = class
  public
    const
      SizeOfDataType = sizeof(_DATA_TYPE_);
    type
      TMyArray = array [0..SizeOfDataType] of _DATA_TYPE_;
  end;

begin
  Writeln(high(TemplateList<Integer>.TMyArray));
  Writeln(high(TemplateList<Double>.TMyArray));
  Readln;
end.

やや望ましくない出力が生成されます。

0
0

そのため、コンパイルのジェネリック フェーズで配列の境界がチェックされるだけでなく、配列の境界がそのフェーズで固定され、型パラメーター サイズのプレース ホルダー値を使用して固定されるようです。これが意味することは、データ型のサイズに基づいて変化する配列境界を達成することは期待できないということです。

XE3 にも同じ動作が見られますが、確認する XE4 は手元にありません。

個人的には、これは QC レポートが必要なコンパイラの設計上の欠陥であると感じています。


私の意見では、これを解決する唯一の実行可能な方法は、配列の境界を指定しようとすることをあきらめることです。私は次のように宣言します:

type
  TemplateList<_DATA_TYPE_> = class
  public
    type
      TIntList = array[0..0] of _DATA_TYPE_;
      PIntList = ^TIntList;
  private
    FList: PIntList;
    FCount: Integer;
  end;

明らかに、この単元では範囲チェックを無効にする必要がありますが、範囲チェックは元のコードに何の恩恵も与えないため、それほど難しいことではありません。

于 2013-07-08T12:03:46.547 に答える