Migration du code Delphi en mobile depuis le bureau

De RAD Studio
Aller à : navigation, rechercher

Remonter à Considérations Delphi pour les applications multi-périphériques


Cette rubrique décrit comment migrer du code Delphi existant pour utiliser les compilateurs mobiles Delphi :

Eliminer les types de données qui ne sont pas supportés par les compilateurs mobiles Delphi

Un code utilisant l'un des types non supportés suivants doit être éliminé ou réécrit afin d'utiliser un type de remplacement :

WideString, AnsiString, ShortString, AnsiChar, PAnsiChar, PWideChar, Openstring

Type utilisé dans les applications de bureau
(basé sur 1)

Type à utiliser dans les apps mobiles
(basé sur 0)

System.WideString

System.String

System.AnsiString, System.ShortString

Eliminer l'usage.

Considérer l'utilisation de 'array of byte'

System.AnsiChar

System.Char, System.Byte, System.UInt8

System.PAnsiChar, System.PWideChar

System.SysUtils.TStringBuilder, System.String, System.MarshaledString

System.Openstring

Considérer l'utilisation de 'array of byte'

Vous trouverez ci-dessous des détails relatifs au remplacement de ces types non supportés.

WideString

Dans certains cas, WideString peut être remplacé par String et TWideStringDynArray peut être remplacé par TStringDynArray.

Si vous devez utiliser WideString sur la plate-forme mobile pour une raison quelconque, vous devez le marshaller en gérant la longueur sur 4 octets, et en traitant la séquence de caractères Unicode et les deux caractères null représentant le terminateur de chaîne. Pour consulter un exemple utilisant ShortString, voir l'exemple de code ShortStringToString (Delphi).

AnsiString et ShortString

Ce regroupement inclut AnsiString et la longueur explicite ShortStrings en utilisant la syntaxe : type TStr = string[127];.

Remarque : Les types "dérivés" UTF8String et RawByteString sont pris en charge dans les périphériques mobiles.

Retirez ou changez les instances de AnsiString et ShortString, selon l'usage original. Dans certains cas, 'array of byte' (tel que System.Types.TByteDynArray) est suffisant.

Dans la plupart des cas, vous devez décoder et encoder l'ancien format si nécessaire. La majorité des utilisations de AnsiString peuvent être directement remplacées par le type par défaut String. La plupart des informations enregistrées en externe avec ce type utilisaient des flux ou des classes utilisant des flux, telles que TStringList ou des classes similaires. Ces types de classes supportent BOM (Byte Order Marks) et décodent si nécessaire automatiquement. Là où une conversion est nécessaire, utilisez la classe TEncoding pour obtenir les octets directement. En fait, TStrings, la classe de base de TStringList, supporte la spécification explicite des instances de TEncoding dans des méthodes telles que TStrings.SaveToFile et TStrings.LoadFromFile, afin que votre programme puisse utiliser le type String normal quel que soit l'encodage ultime nécessaire pour le stockage en dehors du programme.

Pour le code qui utilisait des chaînes courtes, utilisez TEncoding pour gérer la différence entre la représentation caractère UTF16 utilisée dans le type String en cours et la représentation ANSI sur 8 bits de l'ancienne chaîne courte.

Voir l'exemple de code ShortStringToString (Delphi).

AnsiChar

Utilisez (Wide)Char ou Byte(UInt8) pour remplacer AnsiChar.

Selon la sémantique originale :

  • Si la sémantique originale est 'Caractère', utilisez Char avec la conversion UNICODE.
  • Si la sémantique originale est 'stockage 8 bits', utilisez le type Byte.

PAnsiChar et PWideChar

Si ces types pointent sur une chaîne Ansi/Unicode, utilisez l'objet String ou TStringBuilder au lieu du type PAnsiChar/PWideChar.

Si la sémantique originale est associée à un appel API, remplacez-le par la fonctionnalité de marshaling API. Typiquement, System.MarshaledString est suffisant.

Openstring

System.Openstring est un ancien élément de langage. System.Generics.Defaults utilise actuellement le type OpenString, mais il est rarement utilisé ailleurs dans RAD Studio.

"array of byte" peut souvent être utilisé à la place de OpenString, comme vu dans la fonction ShortStringToString() de l'exemple de code ShortStringToString (Delphi). "Array of byte", tel qu'utilisé ici, constitue un paramètre tableau ouvert et accepte des tableaux d'octets de n'importe quelle longueur, exactement comme OpenString autorisait les chaînes de n'importe quelle taille déclarée.

Voir http://www.drbob42.com/uk-bug/hood-03.htm (EN)

Utiliser des chaînes basées sur 0

Pour les compilateurs mobiles Delphi, les chaînes présentent une indexation basée sur 0. Par ailleurs, il est fort probable qu'elles deviendront immuables (constantes) dans le futur.

Indexation des chaînes dans les compilateurs Delphi
Compilateurs Delphi Indexation des chaînes

Compilateurs mobiles Delphi :


Basée sur 0

(l'index de début du premier caractère d'une chaîne est zéro)

Compilateurs de bureau Delphi :


Basée sur 1

(l'index de début du premier caractère d'une chaîne est 1)

Nous vous recommandons de réécrire tout code supposant que les chaînes sont immuables ou basées sur 1.

  • Chaînes basées sur 0 : Pour tout index basé sur 1 utilisé pour accéder aux éléments caractère d'une chaîne, réécrivez le code afin d'utiliser l'indexation basée sur 0 (voir l'exemple ci-dessous).
  • Chaînes immuables : Si vous voulez changer un caractère à l'intérieur d'une chaîne immuable, vous devez fractionner cette chaîne en plusieurs parties et combiner ces parties, ou utiliser un TStringBuilder.
    Par exemple, l'opération commune suivante (indexation dans une chaîne et modification de la chaîne) ne peut pas être effectuée avec des chaînes immuables :
    S[1] := 'A';
Si vous utilisez une opération chaîne comme celle-ci, les compilateurs mobiles Delphi émettent l'avertissement W1068 La modification des chaînes in-situ peut ne pas être supportée à l'avenir (Delphi). A un moment, cet avertissement sera remplacé par une erreur. Vous pouvez le convertir en erreur dès maintenant sur la page Conseils et avertissements dans Options de projet.

Nous recommandons l'utilisation de TStringHelper pour gérer les chaînes dans les apps mobiles et de bureau

L'assistance de classe ou d'enregistrements System.SysUtils.TStringHelper est utile pour la manipulation des chaînes et l'écriture de code indépendant de la plate-forme. Vous pouvez utiliser TStringHelper dans tous les environnements (de bureau et mobile). Comme TStringHelper effectue des conversions automatiques, vous pouvez utiliser TStringHelper avec des chaînes basées sur 0 et des chaînes basées sur 1. En interne, toutes les fonctions et propriétés de TStringHelper sont basées sur 0 dans tous les scénarios.

Certaines des fonctions RTL qui fonctionnent avec des chaînes basées sur 1 ont des remplacements directs dans les fonctions TStringHelper, comme illustré dans le tableau suivant :

Fonction RTL Delphi
(basée sur 1)

Fonction TStringHelper
(basée sur 0)*

System.Pos
TStringHelper.IndexOf
System.Delete
TStringHelper.Remove
System.Copy
TStringHelper.Substring
System.SysUtils.Trim
TStringHelper.Trim
* Les fonctions d'assistance fonctionnent correctement pour les chaînes basées sur 1 et les chaînes basées sur 0.

Cette rubrique contient des exemples de tous les remplacements suggérés ci-dessus (excepté pour Delete-Remove).
Les sous-rubriques suivantes illustrent les changements requis pour migrer votre code avec l'indexation des chaînes basée sur 1 vers une indexation basée sur 0 :

Test des chaînes immuables

Pour tester les chaînes immuables, effectuez l'une des opérations suivantes :

  • Définissez la directive du compilateur {$WARN IMMUTABLE_STRINGS <ON|ERROR>}.
  • Sur la page Conseils et avertissements, définissez l'avertissement "La modification des chaînes in-situ...." sur "true" ou "erreur".

Quand les chaînes sont modifiées in-situ, le message d'avertissement/d'erreur suivant est affiché : W1068 La modification des chaînes in-situ peut ne pas être supportée à l'avenir (Delphi)

Exemple de conversion de chaînes basées sur 1 en chaînes basées sur 0

Voici un exemple montrant comment changer une chaîne basée sur 1 pour un bon fonctionnement dans toutes les plates-formes :

 function Trim(const S: string): string;
 var
   I, L: Integer;
 begin
   L := Length(S);
   I := 1;
   if (L > 0) and (S[I] > ' ') and (S[L] > ' ') then Exit(S);
   while (I <= L) and (S[I] <= ' ') do Inc(I);
   if I > L then Exit('');
   while S[L] <= ' ' do Dec(L);
   Result := Copy(S, I, L - I + 1);
 end;

Utilisation de TStringHelper.Chars pour accéder aux caractères d'une chaîne

Chars est une propriété utile de TStringHelper :

Chars[Index]

Cette propriété en lecture seule peut accéder à tous les caractères d'une chaîne. N'oubliez pas que les chaînes sont toujours basées sur 0 pour les compilateurs mobiles Delphi.

Exemple d'utilisation de la propriété Chars pour accéder à des caractères individuels :

 function Trim(const S: string): string;
 var
   I, L: Integer;
 begin
   L := S.Length - 1;
   I := 0;
   if (L > -1) and (S.Chars[I] > ' ') and (S.Chars[L] > ' ') then Exit(S);
   while (I <= L) and (S.Chars[I] <= ' ') do Inc(I);
   if I > L then Exit('');
   while S.Chars[L] <= ' ' do Dec(L);
   Result := S.SubString(I, L - I + 1);
 end;

Utilisation de System.Low et System.High pour accéder au premier et au dernier index d'une chaîne

Vous pouvez utiliser les routines intrinsèques Delphi High et Low appliquées aux chaînes.

  • Low(s) renvoie 0 dans notre scénario de chaîne basée sur 0, mais renvoie 1 pour une chaîne basée sur 1.
  • High(s) renvoie Length(s) - 1 dans notre scénario de chaîne basée sur 0, mais renvoie Length(s) pour une chaîne basée sur 1.

Pour détecter le premier index d'une chaîne, utilisez :

Low(string)

Par exemple, vous pouvez remplacer cette instruction for couramment utilisée :

for I := 1 to Length(S) do

par cette instruction for :

for I := Low(S) to High(S) do

Pour un autre exemple, quand s = '' (vide) :

  • Low(s) = 0 et High(s) = -1 pour les chaînes basées sur 0.
  • Low(s) = -1 et High(s) = 0 pour les chaînes basées sur 1.

Remplacement de la fonction System.Pos par TStringHelper.IndexOf

La fonction System.Pos fonctionne avec les chaînes basées sur 1, et pas avec les chaînes basées sur 0. A la place de Pos, vous pouvez utiliser TStringHelper.IndexOf. La fonction IndexOf renvoie la position d'index basée sur 0 du paramètre Valeur (un caractère ou une chaîne) si cette chaîne a été trouvée, ou -1 si elle est introuvable.


Exemple :

 s := 'The quick brown fox jumps over the lazy dog'; // s is a string type variable.
 WriteLn(Pos('ow', s));    // 13
 WriteLn(s.IndexOf('ow')); // 12

Remarque : La fonction TStringHelper.IndexOf est similaire à l'implémentation .NET, sauf si la chaîne Valeur est vide, .NET renvoie 0, mais la Delphi RTL renvoie -1.

Remplacement de la fonction System.Copy par TStringHelper.Substring

La fonction System.Copy fonctionne avec les chaînes basées sur 1, et pas avec les chaînes basées sur 0. A la place de Copy, vous pouvez utiliser TStringHelper.Substring :

  function TStringHelper.Substring(StartIndex: Integer; Length: Integer): string;

La fonction Substring renvoie une chaîne équivalente à la sous-chaîne de longueur Length qui commence à StartIndex dans cette instance. Si StartIndex est plus grand ou égal à la longueur de cette instance, Substring renvoie une chaîne vide. Si Length est égal à zéro ou est un nombre négatif, Substring renvoie une chaîne vide.

Exemple

 s := '123456789'; // s is string type variable.
 writeln( Copy(s, 2, 3) );     // 234
 writeln( s.Substring(1, 3) ); // 234

Remarque : La fonction TStringHelper.Substring est similaire à l'implémentation .NET, sauf que .NET déclenche une exception ArgumentOutOfRangeException si StartIndex plus Length indique une position hors de cette instance, ou si StartIndex ou Length est inférieur à zéro. La RTL Delphi, d'autre part, ne déclenche pas d'exception. Pour la condition ci-dessus, Substring renvoie une chaîne vide.

Mise à jour des types tableau

Effectuez la mise à jour de toutes les déclarations de tableaux afin de les rendre dynamiques. Utilisez l'une des syntaxes suivantes :

var x: array of Integer;
x: TArray<Integer>;

Il existe des cas où une structure (enregistrement) doit être transmise à une fonction externe et qu'elle contient un tableau déclaré avec une longueur spécifique. C'est particulièrement vrai pour les tableaux de caractères déclarés "in-situ" à l'intérieur de la structure. Dans ce cas, et seulement dans ce cas, ce qui suit est autorisé :

type
   rec = record
    Flags: Integer;
    Chars: array[MAX_PATH] of Char;
  end;

Dans les cas où une fonction externe prend un tableau directement, utilisez à la place un tableau dynamique. Pour les tableaux de caractères UTF8 :

Utiliser un appel de fonction dans un bloc try-except pour empêcher les exceptions matérielles non interceptées

Avec le compilateur pour les périphériques iOS, les blocs except peuvent intercepter une exception matérielle uniquement si le bloc try contient une méthode ou un appel de fonction. Le back-end LLVM du compilateur est légèrement différent dans la mesure où il ne peut pas effectuer de retour si aucune méthode/fonction n'est appelée dans le bloc try.

Ainsi, voici comment structurer un bloc try-except capable d'intercepter une exception matérielle :

var
  P: ^Integer = nil;

procedure G1;
begin
  P^ := 42;
end;

begin
  try
    G1;
  except
    writeln('Catch:G1 - pass');
  end;
end.

Même si un bloc de code source semble contenir un appel de fonction, cela peut ne pas être le cas si la fonction est une fonction inlined. Au moment où LLVM génère des instructions machine, le processus inline a déjà eu lieu et il n'existe plus d'appel de fonction dans le bloc try.

Utiliser les intrinsèques atomiques au lieu du langage assembleur

Les compilateurs mobiles Delphi ne supportent pas un assembleur intégré. Si vous avez besoin d'échanger, de comparer-et-d'échanger, d'incrémenter et de décrémenter atomiquement des valeurs en mémoire, vous pouvez utiliser les nouvelles fonctions intrinsèques atomiques.

Les opérations atomiques sont utilisées pour implémenter des primitives de verrouillage multithreads et fournir les primitives nécessaires à l'implémentation de structures "exemptes de verrou". Les types d'opérations nécessaires sont implémentés en tant que fonctions standard ou fonctions "intrinsèques".

Dans une application multiplate-forme, les intrinsèques atomiques peuvent être utilisés dans {$IFDEF} pour la conditional AUTOREFCOUNT ou NEXTGEN..

Fonctions intrinsèques atomiques

Voici les fonctions intrinsèques atomiques supportées par les compilateurs mobiles Delphi :

Comptage automatique des références

Les compilateurs mobiles Delphi (DCCIOS32, DCCIOS32ARM et DCCAARM) utilisent le comptage automatique des références (ARC) pour les classes, un schéma de comptage de références qui est différent du schéma utilisé par les compilateurs de bureau Delphi (DCC32, DCC64 et DCCOSX). Toutefois, tous les compilateurs Delphi prennent en charge ARC pour les interfaces, les chaînes et les tableaux dynamiques. En réalité, les compilateurs mobiles Delphi étendent simplement ARC aux classes. ARC inclut la disposition et la gestion de mémoire automatiques.

Remarque : Pour les compilateurs Delphi qui prennent en charge ARC, les instances d'objet qui font référence les unes aux autres peuvent effectivement verrouiller la mémoire sans que l'une des références soit marquée avec l'attribut de référence faible.

Pour de plus amples informations sur ARC et les références faibles, voir :

Voir aussi