Подтвердить что ты не робот

Как работать с строками на основе 0 с обратной совместимостью с Delphi 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.

4b9b3361

Ответ 1

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

Как отметил Реми Лебо, 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 возможных решения:

а. Каждый раз, когда туда или рядом с ним перемещаются элементы или теги, размещайте вокруг него 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). Обратите внимание: если цель мобильна, вы не должны применять ZEROBASEDSTRINGS OFF глобально в своем коде, так как функции RTL (например, TStringHelper) возвращают результаты на основе 0, потому что мобильное RTL скомпилировано с помощью ZEROBASEDSTRINGS ON.

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

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

Ответ 2

Если вы хотите поддерживать версии, в которых используются одни строки, не определяйте 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 является локальным, и что считается его состоянием в том месте, где будут использоваться такие функции. На самом деле это просто невозможно реализовать эти функции значимым образом.

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

Ответ 3

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

Единственными реальными проблемами, которые вы должны соблюдать, являются такие вещи, как индексы жесткого кодирования, такие как пример вашего цикла. К сожалению, без доступа к Low/High(String) в более старых версиях Delphi единственный способ написать такой код переносимым образом - использовать IFDEF s, например:

{$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}.

Ответ 4

Как определить это как файл 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}

Ответ 5

Как сказал 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 name и AnsiString/WideString типа данных.

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

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

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