[我试图以最小的更改将当前的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开始。
如果要支持使用基于一个字符串的版本,则不要定义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
。
RTL的所有预先存在的功能(ZEROBASEDSTRINGS
,Pos()
等)仍然(并将保持)基于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;
检查。
这是两个答案的总和:
正如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
。
如何将其定义为inc文件?根据要支持的Delphi版本放置其他ifdef。由于此代码仅适用于ZBS之前的版本,因此可以在字符串上使用Low(other type)
和Low/High
,因此StringLow
定义仅是本地的,不会出现问题。
您可以在本地包含此代码(作为嵌套例程),这样可以减少与ZEROBASEDSTRINGS
和Low
发生冲突的风险。
High
作为LU RDZEROBASEDSTRINGS
System.Low
和System.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告诉它)
好消息:Delphi 10.4将所有平台的ZEROBASEDSTRINGS默认设置为OFF!