19

プロジェクトをさまざまな Delphi バージョンから Win32 に、XE5 から Android にクロスコンパイルできるように、現在の Delphi 7 Win32 コードを最小限の変更で Delphi XE5 Android に変換しようとしています。

XE5 以降、将来に向けて言語に重大な変更があります。そのような変更の 1 つは、0 から始まる文字列です。

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

3

RTL の既存の関数 ( Pos()Copy()など) はすべて、下位互換性のために 1 ベースのままです (今後も維持されます)。0 ベースの機能はTStringHelper、XE3 で導入された新しいレコード ヘルパーを介して公開されます。古いコードでは使用されないため、何も壊れません。

あなたが注意しなければならない唯一の落とし穴は、あなたのループの例のように、ハードコーディングされたインデックスのようなものです。残念ながら、古いバージョンの 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

これはむしろ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;

考えられる解決策は 2 つあります。

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サードパーティのコードによって台無しにされないため、コードで変更しなければ問題ありません (StringLow呼び出し側のコードが同じ設定である限り、上記は問題なく動作しZEROBASEDSTRINGSます)。ターゲットがモバイルの場合、モバイル RTL は でコンパイルされるためZEROBASEDSTRINGS OFF、RTL 関数 (例: TStringHelper) は 0 ベースの結果を返すため、コードにグローバルに適用しないでください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 より前のバージョンのみを使用できるようにするためのものでLowあり、文字列に対してのみ定義されているためHigh、問題が発生することはありません。ZEROBASEDSTRINGS

このコードをローカルに (ネストされたルーチンとして) 含めることができます。これにより、System.Lowおよび と衝突するリスクが軽減されます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 RDが上 Lowで述べたようにHigh、文字列の関数は XE3 でのみ導入されました。では、以前のバージョンの Delphi で見逃されていた関数をどのように使用できるのでしょうか? いつもと同じように - もし関数が見落とされていたら - 行って書いてください!

条件付きコンパイルを使用して、XE3 バージョンを超える Delphi のこれらの互換性追加機能のみを有効にする必要があります。>=比較を使用して、1つの方法が他の回答で説明されています。別の通常の方法は、定義ファイルを再利用することです。jedi.inc

次に、以前の Delphi バージョンでは、次のような独自の実装を追加します。

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

指定子に注意してoverloadください - それがトリックを可能にするものです。忘れないでください!

2007 年まで Delphi 7 では、Low/Highfn 名とAnsiString/WideStringデータ型の組み合わせをカバーする 4 つの関数を作成する必要がありました。

UnicodeStringXE2 までの Delphi 2009 では、データ型にさらに 2 つの関数を追加する必要があります。

またinline、それをサポートする Delphi バージョンの関数にマークを付けます (ここjedi.incでも便利です。

のサポートが必要ないことを願っていますが、必要なUTF8String場合は、今すぐ何をすべきかがわかります (オーバーロード時にコンパイラが AnsiString からそれを伝えることができれば...)。

于 2013-10-22T08:09:26.763 に答える