Как работать с с 0 строки в обратную совместимость с Делфи XE5?

Я пытаюсь преобразовать мой текущий код Delphi 7 Win32 в Delphi XE5 Android с минимальными изменениями, так что мой проект может быть скомпилирован в Win32 из ряда версий Delphi и Android от XE5.

начиная с 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. Я знаю, что я еще могу отключить ZEROBASEDSTRINGS определяют в XE5, но это нежелательное решение, так как в XE6 это определение, вероятно, исчезнет, и все строки будут вынуждены быть основанными на 0.

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;

обновление

как указывает Реми, приведенный выше код не годится. Это потому что ZEROBASEDSTRINGS является локальным, и значение имеет его состояние в том месте, где будут использоваться такие функции. На самом деле просто невозможно реализовать эти функции в a осмысленный способ.

Итак, я считаю, что для кода, который должен быть скомпилирован с использованием устаревших компиляторов, а также мобильных компиляторов, у вас нет выбора, кроме как отключить. ZEROBASEDSTRINGS.


все существующие функции RTL (Pos(), Copy() и т. д.) по-прежнему (и останутся) 1-based для обратной совместимости. Функциональность на основе 0 предоставляется через new TStringHelper помощник записи, который был представлен в XE3, который более старый код не будет использовать, поэтому ничего не ломается.

единственные реальные gotchas вы должны следить за такие вещи, как жестко закодированные индексы, такие как ваш пример цикла. К сожалению, без доступа к Low/High(String) в старых Delphi версии, единственный способ написать такой код портативным способом-использовать 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} проверка.


это скорее итог двух ответов:

как указал Реми Лебо,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

В. К. ZEROBASEDSTRINGS условный per-block он никогда не портится сторонним кодом, и если вы не измените его в своем коде, вы в порядке (выше StringLow будет работать нормально, пока код вызывающего абонента имеет тот же ZEROBASEDSTRINGS настройки). Обратите внимание, что если target является мобильным, вы не должны применять ZEROBASEDSTRINGS OFF глобально в вашем коде, так как функции RTL (например,TStringHelper) вернет результаты на основе 0, потому что мобильный RTL скомпилирован с ZEROBASEDSTRINGS ON.

на стороне Примечание - можно было бы предложить написать перегруженные версии Low/High для более старых версий Delphi, но тогда Low(other type) (где type-массив чего-то) перестает работать. Похоже, с Low/High не являются обычными функциями, то не могут быть перегружены, что просто.

TL; DR-использовать пользовательские StringLow и не менять ZEROBASEDSTRINGS в коде.


как определить этот файл Inc? Поместите дополнительные ifdefs в зависимости от того, какие версии Delphi вы хотите поддерживать. Поскольку этот код предназначен только для версий до 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}

As LU RD сказали выше Low и High функции для строки были введены только в XE3. Итак, как вы можете использовать функции в более ранних версиях Delphi, которые пропущены? Точно так же, как и всегда - если функция пропущена - идите и напишите ее!

вы должны активировать только эти дополнения совместимости для Delphi за пределами версии XE3, используя условную компиляцию. Один из способов описан в других ответах, используя > = сравнение. Другим обычным способом было бы повторное использование jedi.inc определения файла.

тогда для более ранних версий Delphi вы бы добавили свои собственные реализации тех, как

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

обратите внимание overload спецификатор - это то, что сделает трюк возможным, не забывайте об этом!

вам придется написать 4 функции для Delphi 7 до 2007 года, охватывающие комбинации Low/High имя Fn и AnsiString/WideString тип данных.

для Delphi 2009 до XE2 вам придется добавьте еще две функции для UnicodeString тип данных.

а также отметьте эти функции inline для тех версий Delphi, которые поддерживают его (это где jedi.inc снова пригодится.

Надеюсь, вам не нужно supprot для UTF8String, но если вы это сделаете - вы знаете, что делать с этим сейчас (если компилятору удастся сказать это из AnsiString при перегрузке...)