programming - delphi wikipedia
¿Cómo trabajar con cadenas basadas en 0 de una manera compatible con versiones anteriores desde Delphi XE5? (5)
¿Qué hay de definir esto como un archivo inc? Ponga ifdefs adicionales según las versiones de Delphi que desee admitir. Dado que este código es solo para las versiones anteriores a la ZBS para permitir el uso de Low
y High
en cadenas, no se encontrará con el problema con las ZEROBASEDSTRINGS
solo definen como locales.
Puede incluir este código localmente (como rutinas anidadas), lo que reduce el riesgo de colisión con System.Low
y 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}
Estoy tratando de convertir mi código de Delphi 7 Win32 actual a Delphi XE5 Android con cambios mínimos, para que mi proyecto pueda ser compilado de manera cruzada a Win32 desde una variedad de versiones de Delphi y Android desde XE5.
A partir de XE5 hay cambios de última hora en el lenguaje orientado hacia el futuro. Uno de tales cambios es cadenas basadas en cero.
En versiones anteriores con cadenas basadas en 1, el siguiente código era correcto:
function StripColor(aText: string): string;
begin
for I := 1 to Length(aText) do
Pero ahora esto obviamente no está bien. La solución sugerida es utilizar:
for I := Low(aText) to High(aText) do
De esta manera XE5 Win32 maneja cadenas basadas en 1 y XE5 Android maneja cadenas basadas en 0 correctamente. Sin embargo, hay un problema: las versiones anteriores de Delphi (por ejemplo, XE2) generan un error en dicho código:
E2198 Low cannot be applied to a long string
E2198 High cannot be applied to a long string
Tengo bastante código de manipulación de cadenas. Mi pregunta es: ¿cómo modificar y mantener el código anterior para poder compilarlo en Delphi 7 Win32 y Delphi XE5 Android?
PD: Sé que todavía puedo deshabilitar ZEROBASEDSTRINGS define en XE5, pero esa es una solución no deseada, ya que en XE6 esta definición probablemente se habrá eliminado y todas las cadenas serán obligadas a estar basadas en 0.
Como LU RD dijo anteriormente, las funciones Low
y High
para cadena solo se introdujeron en XE3. Entonces, ¿cómo se pueden usar las funciones en las versiones anteriores de Delphi, que se pierden? De la misma manera que siempre, si se pierde la función, ¡vaya y escríbala!
Solo debe activar esas adiciones de compatibilidad para Delphi más allá de la versión XE3, usando la compilación condicional. Una forma se describe en otras respuestas, usando> = comparación. Otra forma habitual sería reutilizando el archivo de definiciones jedi.inc
.
Luego, para versiones anteriores de Delphi, agregaría sus propias implementaciones de esas, como
function Low(const S: AnsiString): integer; overload;
Preste atención al especificador de overload
: es lo que haría posible el truco, ¡no lo olvide!
Tendría que escribir 4 funciones para Delphi 7 hasta 2007, cubriendo combinaciones de nombre de fn Low/High
y tipo de datos AnsiString/WideString
.
Para Delphi 2009 hasta XE2, tendría que agregar dos funciones más para el tipo de datos UnicodeString
.
Y también marque esas funciones en inline
para esas versiones de Delphi, que lo admiten (aquí es donde jedi.inc
vuelve a ser útil).
Esperemos que no necesite supprot para UTF8String
, pero si lo hace, ya sabe qué hacer al respecto (si el compilador pudiera decirlo desde AnsiString cuando se sobrecarga ...)
Esto es más bien un resumen de las dos respuestas:
Como señaló Remy Lebeau, ZEROBASEDSTRINGS
es un condicional por bloque. Eso significa que el siguiente código no funcionará como se esperaba :
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;
Hay 2 soluciones posibles:
R. Cada vez que hay elementos de cadena, el acceso o la iteración colocan una IFDEF
a su alrededor, lo que es un gran desorden para el código, pero funcionará correctamente independientemente de la configuración de ZEROBASEDSTRINGS
a su alrededor:
for I := {$IFDEF XE3UP}Low(aText){$ELSE}1{$ENDIF} to {$IFDEF XE3UP}High(aText){$ELSE}Length(aText){$ENDIF} do
B. Dado que el condicional ZEROBASEDSTRINGS
es per-block
, el código de terceros nunca lo estropea y si no lo cambia en su código, está bien (por encima de StringLow
funcionará bien siempre y cuando el código de la persona que llama tenga la misma configuración de ZEROBASEDSTRINGS
) . Tenga en cuenta que si el objetivo es móvil, no debe aplicar ZEROBASEDSTRINGS OFF
globalmente en su código ya que las funciones RTL (por ejemplo, TStringHelper
) devolverán resultados basados en 0 porque la RTL móvil se compila con ZEROBASEDSTRINGS ON
.
En una nota al margen : se podría sugerir escribir una versión sobrecargada de Low/High
para versiones anteriores de Delphi, pero luego Low(other type)
(donde el tipo es una matriz de algo) deja de funcionar. Parece que dado que las funciones Low/High
no son habituales, entonces no se pueden sobrecargar tan fácilmente.
TL; DR: use StringLow
personalizado y no cambie ZEROBASEDSTRINGS
en su código.
Si desea admitir versiones que utilizan cadenas basadas en uno, no defina ZEROBASEDSTRINGS
. Ese es el propósito de ese condicional.
No hay ninguna indicación de que sepa que el condicional se eliminará en el corto plazo. Fue introducido en XE3 y ha sobrevivido a dos lanzamientos posteriores. Si Embarcadero lo elimina, ninguno de sus clientes de Win32 no se actualizará y quebrarán. Embarcadero tiene un historial de mantener la compatibilidad. Todavía puedes usar objetos TP y cadenas cortas. Espere que este condicional viva tanto como lo hace el compilador de escritorio.
De hecho, toda la evidencia apunta a que los compiladores móviles retienen el soporte para una cadena basada en la indexación. Todas las funciones de cadena de utilidad como Pos
utilizan índices basados en uno, y continuarán haciéndolo . Si Embarcadero realmente va a eliminar el soporte para una cadena basada en indexación, también eliminará Pos
. No creo que sea probable en el corto plazo.
Tomando su pregunta como valor nominal, es trivial escribir funciones que devuelven los índices alto y bajo de una cadena. Solo usas una IFDEF
en la versión del compilador.
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;
Actualizar
Como Remy señala, el código anterior no es bueno. Esto se debe a que ZEROBASEDSTRINGS
es local y lo que cuenta es su estado en el lugar donde se usarían dichas funciones. De hecho, simplemente no es posible implementar estas funciones de manera significativa.
Por lo tanto, creo que para el código que debe compilarse utilizando compiladores heredados, así como los compiladores móviles, no tiene más remedio que deshabilitar. ZEROBASEDSTRINGS
.
Todas las funciones preexistentes de RTL ( Pos()
, Copy()
, etc.) aún están (y seguirán siendo) basadas en 1 para la compatibilidad con versiones anteriores. La funcionalidad basada en 0 se expone a través del nuevo TStringHelper
registro TStringHelper
que se introdujo en XE3 , que el código anterior no utilizará, por lo que no se rompe nada.
Los únicos errores reales que debe tener en cuenta son cosas como los índices codificados, como su ejemplo de bucle. Desafortunadamente, sin acceso a Low/High(String)
en versiones anteriores de Delphi, la única forma de escribir dicho código de manera portátil es usar IFDEF
s, por ejemplo:
{$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;
O:
{$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;
Las expresiones condicionales se introdujeron en Delphi 6, por lo tanto, si no necesita admitir una versión anterior a Delphi 7 y no es compatible con otros compiladores como FreePascal, entonces puede omitir la comprobación {$IFDEF CONDITIONALEXPRESSIONS}
.