1

在 Delphi 中使用包含文件制作半通用容器有一个古老的技巧。

请参阅http://www.delphikingdom.com/asp/viewitem.asp?catalogid=453&mode=print并从 3d 列表开始以掌握这个想法。

interface然而,在单元和部分中有两个相互依赖的 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\>...

聚苯乙烯。有趣的事情,用复制粘贴重新形成内部类型

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

将错误更改为“需要 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

这表明您的常量声明具有用于数组类型边界检查的占位符值,但在泛型实例化后具有真实值。因此,如果编译器将数组类型边界检查推迟到实例化,那么就编译器警告而言一切都会好起来的。但即便如此,代码也不会按照您的期望和期望进行。这个程序:

{$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 回答