Migration du code Object Pascal en mobile depuis le bureau

De Appmethod Topics
Aller à : navigation, rechercher

Remonter à Développement d'applications mobiles iOS

Remonter à Développement d'applications mobiles Android

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

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

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 applications mobiles
(basé sur 0)

System.WideString

System.String

System.AnsiString, System.ShortString, System.RawByteString, System.UTF8String

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.

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 (Object Pascal).

AnsiString et ShortString

Ce regroupement inclut les types "dérivés" tels que UTF8String et RawByteString, et la longueur explicite ShortStrings en utilisant la syntaxe :

type TStr = string[127];

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 (Object Pascal).

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 Appmethod.

"array of byte" peut souvent être utilisé à la place de OpenString, comme vu dans la fonction ShortStringToString() de l'exemple de code ShortStringToString (Object Pascal). "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

Utiliser des chaînes basées sur 0

Pour les compilateurs mobiles Object Pascal, 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 Object Pascal
Compilateurs Object Pascal Indexation des chaînes

Compilateurs mobiles Object Pascal :

Basée sur 0

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

Compilateurs de bureau Object Pascal :

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 Object Pascal émettent l'avertissement W1068 La modification des chaînes in-situ peut ne pas être supportée à l'avenir (Object Pascal). 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 Projet > Options.

Nous recommandons l'utilisation de TStringHelper pour gérer les chaînes dans les applications 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 Object Pascal
(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éfinir l'avertissement "La modification des chaînes in-situ...." sur "true" ou "error".

Quand les chaînes sont édité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 (Object Pascal)

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 Object Pascal.

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 Object Pascal 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 RTL Object Pascal renvoie -1.

Remplacement de la fonction System.Copy par TStringHelper.Substring

La fonction System.Copy fonctionne avec les chaînes basées sur 1, mais 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 Object Pascal, 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 passée à 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 DCCIOSARM, 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 de DCCIOSARM 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 Object Pascal 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 conditionnelle AUTOREFCOUNT ou NEXTGEN.

Fonctions intrinsèques atomiques

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

Comptage de références automatique

Les compilateurs mobiles Object Pascal (DCCIOS32, DCCIOS32ARM et DCCAARM) utilisent le comptage de références automatique (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 Object Pascal (DCC32, DCC64 et DCCOSX). Toutefois, tous les compilateurs Object Pascal supportent ARC pour les interfaces, les chaînes et les tableaux dynamiques. En réalité, les compilateurs mobiles Object Pascal étendent simplement ARC aux classes. ARC inclut la disposition et la gestion de mémoire automatiques.

La notion de "référence faible" est nouvelle. Vous devez toujours assigner une référence faible à une référence forte (par défaut), afin de garantir que l'objet ne "disparaît" pas quand le compteur de références atteint nil.

Pour de plus amples informations, voir :

Voir aussi