自Delphi XE5起如何以向后兼容的方式使用基于0的字符串?

问题描述 投票:19回答:6

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

[从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中可编译?

P.S。我知道我仍然可以在XE5中禁用ZEROBASEDSTRINGS定义,但这是不希望的解决方案,因为在XE6中,该定义可能会消失,并且所有字符串都将被强制从0开始。

delphi delphi-7 delphi-xe5
6个回答
5
投票

如果要支持使用基于一个字符串的版本,则不要定义ZEROBASEDSTRINGS。这就是有条件的目的。

没有迹象表明我知道该条件将很快被删除。它是在XE3中引入的,并且在两个后续发行版中均幸免于难。如果Embarcadero删除它,则他们的Win32客户将不会升级,他们会破产。 Embarcadero一直保持兼容性。您仍然可以使用TP对象和短字符串。希望此条件与桌面编译器一样有效。

实际上,所有证据都表明移动编译器保留了对基于一个字符串索引的支持。全部utility string functions like Pos use one based indices, and will continue to do so。如果Embarcadero确实要删除对基于一个字符串的索引的支持,那么他们也将删除Pos。我认为这不可能很快出现。

尽管以书面形式返回字符串的低和高索引的函数很琐碎,但还是要以面值来回答问题。您只需在编译器版本上使用Pos

IFDEF

更新

正如雷米(Remy)指出的那样,以上代码不好。这是因为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; 是本地的,而重要的是在使用此类功能的地方其状态。实际上,不可能以有意义的方式实现这些功能。

因此,对于需要使用传统编译器以及移动编译器进行编译的代码,您只能选择禁用它。 ZEROBASEDSTRINGS


3
投票

RTL的所有预先存在的功能(ZEROBASEDSTRINGSPos()等)仍然(并将保持)基于1的向后兼容性。通过新的Copy()提供了基于0的功能,该旧代码将不再使用,因此不会中断。

您唯一需要注意的真正陷阱是诸如硬编码索引之类的东西,例如您的循环示例。不幸的是,在较旧的Delphi版本中无法访问TStringHelper record helper that was introduced in XE3,以可移植的方式编写此类代码的唯一方法是使用TStringHelper,例如:

Low/High(String)

或:

IFDEF

有条件表达式是在Delphi 6中引入的,因此,如果您不需要支持早于Delphi 7的版本,并且不需要支持FreePascal等其他编译器,则可以省略{$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; 检查。


2
投票

这是两个答案的总和:

正如Remy Lebeau指出的那样,{$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; 是每个块的条件。这意味着以下代码将无法按预期工作

{$IFDEF CONDITIONALEXPRESSIONS}

有两种可能的解决方案:

A。每次有字符串项访问或迭代时,都在其周围放置一个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; 的设置如何,它都可以正常工作:

IFDEF

B。由于ZEROBASEDSTRINGS条件为for I := {$IFDEF XE3UP}Low(aText){$ELSE}1{$ENDIF} to {$IFDEF XE3UP}High(aText){$ELSE}Length(aText){$ENDIF} do ,因此它永远不会受到第三方代码的破坏,并且如果您不更改代码中的代码,就可以了(只要调用者代码具有相同的[ C0]设置)。请注意,如果目标是移动的,则不应在代码中全局应用ZEROBASEDSTRINGS,因为RTL函数(例如per-block)将返回基于0的结果,因为移动的RTL是使用StringLow编译的。

附带说明-可能建议为较旧版本的Delphi编写ZEROBASEDSTRINGS的重载版本,但随后ZEROBASEDSTRINGS OFF(类型为某种数组)将停止工作。似乎由于TStringHelper不是常用函数,因此不能简单地重载。

TL; DR-使用自定义ZEROBASEDSTRINGS ON,不要在代码中更改Low/High


2
投票

如何将其定义为inc文件?根据要支持的Delphi版本放置其他ifdef。由于此代码仅适用于ZBS之前的版本,因此可以在字符串上使用Low(other type)Low/High,因此StringLow定义仅是本地的,不会出现问题。

您可以在本地包含此代码(作为嵌套例程),这样可以减少与ZEROBASEDSTRINGSLow发生冲突的风险。

High

1
投票

作为LU RDZEROBASEDSTRINGSSystem.LowSystem.High函数仅在XE3中引入。那么如何在早期的Delphi版本中使用缺少的功能呢?与往常一样,如果缺少该功能,请去编写它!

您仅应使用条件编译为XE3版本之后的Delphi激活那些兼容性。在其他答案中使用> =比较描述了一种方法。另一种常用的方法是重用{$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}

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

told above

请注意Low说明符-这将使技巧成为可能,请不要忘记它!

在2007年之前,您必须为Delphi 7编写4个函数,涵盖High fn名称和jedi.inc definitions file.数据类型的组合。

对于在XE2之前的Delphi 2009,您必须为jedi.inc数据类型添加另外两个函数。

并且还要为支持该功能的那些Delphi版本标记功能function Low(const S: AnsiString): integer; overload; (这是overload再次派上用场的地方。

希望您不需要Low/High的支持,但是如果您这样做了,您现在就知道该怎么办(如果编译器在重载时设法通过AnsiString告诉它)


0
投票

好消息:Delphi 10.4将所有平台的ZEROBASEDSTRINGS默认设置为OFF!

© www.soinside.com 2019 - 2024. All rights reserved.