19

我正在尝试将我当前的 Delphi 7 Win32 代码以最小的更改转换为 Delphi XE5 Android,以便我的项目可以从一系列 Delphi 版本交叉编译为 Win32,并从 XE5 交叉编译为 Android。

从 XE5 开始,针对未来的语言发生了重大变化。其中一项更改是从零开始的字符串。

在具有基于 1 的字符串的旧版本中,以下代码是正确的:

function StripColor(aText: string): string;
begin
  for I := 1 to Length(aText) do

但现在这显然是不对的。建议的解决方案是使用:

for I := Low(aText) to High(aText) do

这样,XE5 Win32 处理基于 1 的字符串,而 XE5 Android 处理正确的基于 0 的字符串。但是有一个问题 - 以前的 Delphi 版本(例如 XE2)在这样的代码上输出错误:

E2198 Low cannot be applied to a long string
E2198 High cannot be applied to a long string

我有很多字符串操作代码。我的问题是 - 如何修改并保持上述代码可在 Delphi 7 Win32 和 Delphi XE5 Android 中编译?

PS 我知道我仍然可以在 XE5 中禁用 ZEROBASEDSTRINGS 定义,但这是不受欢迎的解决方案,因为在 XE6 中这个定义可能会消失,并且所有字符串都将被迫从 0 开始。

4

5 回答 5

5

如果您想支持使用基于字符串的版本,请不要定义ZEROBASEDSTRINGS. 这就是那个条件的目的。

没有迹象表明我知道条件将很快被删除。它是在 XE3 中引入的,并在随后的两个版本中幸存下来。如果 Embarcadero 删除它,他们的 Win32 客户都不会升级,他们将破产。Embarcadero 在保持兼容性方面有着良好的记录。您仍然可以使用 TP 对象和短字符串。只要桌面编译器存在,这个条件就可以存在。

事实上,所有证据都指向移动编译器保留对基于单的字符串索引的支持。所有实用字符串函数都Pos使用基于一个的索引,并将继续这样做。如果 Embarcadero 真的要取消对基于一个的字符串索引的支持,他们也会取消Pos。我不相信这可能很快就会发生。

尽管编写返回字符串的低索引和高索引的函数是微不足道的,但从表面上看你的问题。您只需IFDEF在编译器版本上使用。

function StrLow(const S: string): Integer; inline;
begin
  Result := {$IFDEF XE3UP}low(S){$ELSE}1{$ENDIF}
end;

function StrHigh(const S: string): Integer; inline;
begin
  Result := {$IFDEF XE3UP}high(S){$ELSE}Length(S){$ENDIF}
end;

更新

正如 Remy 指出的那样,上面的代码并不好。那是因为ZEROBASEDSTRINGS它是本地的,重要的是它在使用此类功能的地方的状态。事实上,不可能以有意义的方式实现这些功能。

因此,我相信对于需要使用旧版编译器以及移动编译器进行编译的代码,您别无选择,只能禁用。ZEROBASEDSTRINGS.

于 2013-10-21T07:10:09.280 回答
3

RTL 的所有预先存在的函数(Pos()Copy()等)仍然(并且将保持)基于 1 以实现向后兼容性。基于 0 的功能通过TStringHelperXE3 中引入的新记录助手公开,旧代码将不会使用它,因此没有任何中断。

您必须注意的唯一真正的问题是硬编码索引之类的东西,例如您的循环示例。不幸的是,在较旧的 Delphi 版本中无法访问,Low/High(String)以可移植方式编写此类代码的唯一方法是使用IFDEFs,例如:

{$IFDEF CONDITIONALEXPRESSIONS}
  {$IF CompilerVersion >= 24}
    {$DEFINE XE3_OR_ABOVE}
  {$IFEND}
{$ENDIF}

function StripColor(aText: string): string;
begin
  for I := {$IFDEF XE3_OR_ABOVE}Low(aText){$ELSE}1{$ENDIF} to {$IFDEF XE3_OR_ABOVE}High(AText){$ELSE}Length(aText){$ENDIF} do
    DoSomething(aText, I);
end;

或者:

{$IFDEF CONDITIONALEXPRESSIONS}
  {$IF CompilerVersion >= 24}
    {$DEFINE XE3_OR_ABOVE}
  {$IFEND}
{$ENDIF}

function StripColor(aText: string): string;
begin
  for I := 1 to Length(aText) do
  begin
    DoSomething(aText, I{$IFDEF XE3_OR_ABOVE}-(1-Low(AText)){$ENDIF});
  end;
end;

Delphi 6 中引入了条件表达式,因此如果您不需要支持 Delphi 7 之前的版本,并且不需要支持 FreePascal 等其他编译器,则可以省略该{$IFDEF CONDITIONALEXPRESSIONS}检查。

于 2013-10-21T19:10:14.277 回答
2

这是两个答案的总结:

正如 Remy Lebeau 所指出的,ZEROBASEDSTRINGS是每个块的条件。这意味着以下代码将无法按预期工作

const
  s: string = 'test';

function StringLow(const aString: string): Integer; inline; // <-- inline does not help
begin
  {$IF CompilerVersion >= 24} 
  Result := Low(aString); // Delphi XE3 and up can use Low(s)
  {$ELSE}
  Result := 1;  // Delphi XE2 and below can't use Low(s), but don't have ZEROBASEDSTRINGS either
  {$ENDIF}
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  {$ZEROBASEDSTRINGS OFF}
  Memo1.Lines.Add(Low(s).ToString);        // 1
  Memo1.Lines.Add(StringLow(s).ToString);  // 1
  {$ZEROBASEDSTRINGS ON}
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  {$ZEROBASEDSTRINGS ON}
  Memo1.Lines.Add(Low(s).ToString);        // 0
  Memo1.Lines.Add(StringLow(s).ToString);  // 1  <-- Expected to be 0
  {$ZEROBASEDSTRINGS OFF}
end;

有两种可能的解决方案:

A. 每次有字符串项访问或迭代IFDEF时,都会在其周围ZEROBASEDSTRINGS放置一个

for I := {$IFDEF XE3UP}Low(aText){$ELSE}1{$ENDIF} to {$IFDEF XE3UP}High(aText){$ELSE}Length(aText){$ENDIF} do

B. 由于ZEROBASEDSTRINGS条件是per-block它永远不会被第 3 方代码破坏,如果你不在你的代码中更改它,你就可以了(StringLow只要调用者代码具有相同的ZEROBASEDSTRINGS设置,上面就可以正常工作)。请注意,如果目标是移动的,则不应ZEROBASEDSTRINGS OFF在代码中全局应用,因为 RTL 函数(例如TStringHelper)将返回基于 0 的结果,因为移动 RTL 是用 编译的ZEROBASEDSTRINGS ON

附带说明Low/High- 有人可能会建议为旧版本的 Delphi编写重载版本,但随后Low(other type)(其中类型是数组)停止工作。看起来因为Low/High不是通常的功能,所以不能简单地重载。

TL;DR - 使用自定义StringLow并且不要更改ZEROBASEDSTRINGS您的代码。

于 2013-10-22T06:19:53.643 回答
2

如何将其定义为 inc 文件?根据您想要支持的 Delphi 版本添加额外的 ifdef。由于此代码仅适用于 ZBS 之前的版本,因此可以在字符串上使用LowHigh不会遇到ZEROBASEDSTRINGS仅定义为本地的问题。

您可以在本地包含此代码(作为嵌套例程),这样可以降低与System.Lowand冲突的风险System.High

{$IF CompilerVersion < 24}
function Low(const s: string): Integer; inline;
begin
  Result := 1;
end;

function High(const s: string): Integer; inline;
begin
  Result := Length(s);
end;
{$IFEND}
于 2014-05-08T13:34:20.260 回答
1

正如上面 所说的LU RDLowHigh字符串函数只在 XE3 中引入。那么如何使用早期 Delphi 版本中遗漏的函数呢?就像往常一样——如果错过了这个功能——去写吧!

您应该只使用条件编译为 XE3 版本以外的 Delphi 激活那些兼容性添加。在其他答案中描述了一种方法,使用 >= 比较。另一种常用的方法是重用jedi.inc定义文件。

然后对于早期的 Delphi 版本,您将添加自己的实现,例如

function Low(const S: AnsiString): integer; overload;

注意说明overload符 - 它是使技巧成为可能的原因,不要忘记它!

直到 2007 年,您必须为 Delphi 7 编写 4 个函数,涵盖Low/Highfn 名称和AnsiString/WideString数据类型的组合。

对于 Delphi 2009 到 XE2,您必须为UnicodeString数据类型添加另外两个函数。

inline为那些支持它的 Delphi 版本标记这些功能(这又jedi.inc是方便的地方。

希望您不需要支持UTF8String,但是如果您这样做了-您现在知道该怎么做(如果编译器在重载时设法从 AnsiString 中告诉它...)

于 2013-10-22T08:09:26.763 回答