泛型类中的内部const似乎不是由编译器计算的

时间:2013-07-08 11:50:09

标签: delphi generics compilation const delphi-xe2

使用包含文件在Delphi中创建半通用容器有一个旧的黑客。

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

然而,在单位'interfaceimplementation部分中有两个相互依赖的INC文件会导致麻烦:似乎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;

这给出了TIntList的低限高于高限的错误。我认为,这意味着const MaxListSize被评估为零,但TIntType尝试立即评估,而不是在实际实例化类型时。

我想知道XE3或XE4是否解决了这个问题。如果有一种方法可以在XE2中进行编译而不需要重复工作

PS。使数组0..0和抑制边界检查是通常的解决方案,但它会产生很多脆弱的非检查代码。也许我最终会使用真实TListTList<integer\>而不是......

PPS。有趣的是,用复制粘贴重新构造内部类型

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

将错误更改为“const expression required”。

所以同一个表达式在编译器的一个分支中被认为是const-enough而在另一个分支中被认为是非const ...我想知道它本身是否构成不一致错误。

1 个答案:

答案 0 :(得分:3)

编译器的问题似乎是在编译的通用阶段,sizeof(_DATA_TYPE_)未知。因此编译器似乎使用占位符值0。在实例化泛型类型时,sizeof(_DATA_TYPE_)将替换为true值。但为时已晚。数组类型边界检查在编译的通用阶段执行,此时sizeof(_DATA_TYPE_)0,因此编译器会出现问题。

通过以下代码可以看出这种情况:

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

产生此编译器错误:

[dcc32 Error]: E2098 Division by zero

但是,如果你尝试这个变种:

{$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来检查。

我个人认为这是编译器中的设计缺陷,需要保证质量控制报告。


在我看来,唯一可行的解​​决方法是放弃尝试指定数组边界。我会这样声明:

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

显然你需要在这个单元中禁用范围检查,但这并不是真正的困难,因为范围检查不会对原始代码有任何好处。