IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)


CREATION D'UN TYPE VARIANT SOUS DELPHI 6
  1. PREAMBULE

    Une variable de type Variant peut prendre n'importe quel type. Cela constitue un moyen commode de manipuler une variable sans connaître son type au moment de la compilation.
    ATTENTION, cette commodité se paie au détriment d'un code plus volumineux et plus lent. Toutes les opérations sont autorisées par le compilateur, ce qui fait que la majorité des erreurs apparaissent en exécution. Il faut donc les utiliser avec parcimonie.

    Cet article se propose de présenter les nouveautés introduites dans Delphi 6 en particulier la possibilité de définir ses propres types de variables.

    L'exemple est donné dans le paragraphe 4.

  2. RAPPEL SOUS DELPHI 5

    D'abord quelques exemples …

    Le compilateur détecte une incompatibilité en '12' et 1 var
    I : integer ;
    begin
    I := '12' + 1 ;
    end ;
    Ce code est accepté par le compilateur et il fonctionne correctement. En effet, '12' peut être transformé en 12 et ajouté à 1 pour être stocké dans I. var
    I : integer ;
    V1, V2 : Variant ;
    begin
    V1 := '12' ;
    V2 := 1 ;
    I := V1 + V2 ;
    end ;
    Ce code est accepté par le compilateur et il fonctionne correctement. En effet, 1 peut être transformé en '1' et ajouté à '12' pour être stocké dans I. var
    I : String ;
    V1, V2 : Variant ;
    begin V1 := '12' ;
    V2 := 1 ;
    I := V1 + V2 ;
    end ;
    A l'inverse de l'exemple 2, le code bien qu'accepté par le compilateur provoque une exception. En effet, '12' + 1.2 donne 13.2 qui ne peut pas être stocké dans un I qui est un entier. La solution pour faire fonctionner ce code est de transformer I en Real. var
    I : integer ;
    V1, V2 : Variant ;
    begin
    V1 := '12' ;
    V2 := 1.2 ;
    I := V1 + V2 ;
    end ;
    Le compilateur détecte en ligne une incompatibilité en I et la valeur attendue par ShowMessage var
    I : Integer ;
    begin
    I := 1 ;
    ShowMessage (I) ;
    end :
    Ce code est accepté par le compilateur et il fonctionne correctement. var
    V1 : Variant ;
    begin
    V1 := 1.235 ;
    ShowMessage (V1) ;
    end :

    Ces quelques exemples nous permettent ces quelques constations :

    1. Les variants sont automatiquement transtypés dans le type qui convient le mieux au moment de l'exécution. C'est par exemple ce qui s'est passé dans les exemples 2, 3, 4 et 6.
    2. Dans l'exemple 2, l'opérateur + a donné lieu à une addition d'entiers, dans l'exemple 3, l'opérateur + a donné lieu à une addition (concaténation) de chaînes. Le compilateur a donc implémenté toutes les possibilités d'addition car rappelons le, le compilateur ne savait pas l'avance quel type d'addition il allait avoir à faire.

  3. NOUVEAUTES DE DELPHI 6

    Cette partie ne va pas évoquer toutes les nouvelles fonctionnalités concernant les variants dans Delphi 6 mais uniquement la possibilité de créer des nouveaux types.

    1. Mécanismes d'évaluation d'expression

      Tout le problème lors de la création d'un nouveau type est de définir le comportement du type au sein d'une expression arithmétique. Par exemple

      • 1+2.3 est une addition de réels
      • alors que 1+2 est une addition d'entiers

      Deux constations :

      • Dans ces deux expressions, l'opérateur + a donné lieu a deux sortes d'addition.
      • Dans la première expression, le 1 a été transformé en réel.

      Note :
      Pour évaluer une expression, il est classique de parler d'opérateur unaire (not, - (changement de signe)), d'opérateur binaire (+, -, *, /, div, mod, shl, shr, and, or, xor) et aussi d'opérateur de comparaison (=, <, <=, =>, >, <>). Pour un opérateur binaire, on distingue l'opérande gauche de celle de droite.

      D'une manière générale, les opérateurs binaires ne sont pas symétriques ; ainsi A op B n'est pas forçément équivalent à B op A.

      Pour comprendre comment une expression de Variant est codé en Delphi, examinons le code suivant :

      var
      V1, V2 : MonTypeVariant ;
      S : string ;
      begin
      S := V1 + V2 ;
      end ;

      En suivant pas à pas, le code on fait apparaître les étapes suivantes :

      • V1 est copié dans VT (objet temporaire alloué par le compilateur) par appel à _VarCopy. Avant d'être copié, VT est vidé par appel à _VarClear
      • Appel de _VarAdd pour additionner la partie gauche et droite, ie, VT et V2
      • Enfin, VT est copié dans S après transtypage.

      A chaque opération, le code a trouvé comment vider, copier, ajouter et même transtyper ces variants.

      On le voit, pour que le nouveau type MonTypeVariant puissent être opérationnel comme n'importe quel autre type variant, il faut implémenté un certains nombre de routines de bas niveaux :

      • initialisation
      • copie
      • opérateurs unaires
      • opérateurs binaires
      • opérateurs de comparaisons
      • transtypage

      Remarque
      Les variants permettent aussi de référencer des objets automation. Sur ce type de variant, il est possible de désigner des propriétés ou des méthodes en utilisant la notion pointée classique :

      var
      Word: Variant;
      begin
      Word := CreateOleObject('Word.Basic');
      Word.FileNew('Normal');
      Word.FileSaveAs(Password := 'secret', Name := 'test.doc');
      Word.Visible := False ;
      end ;

      Lors de l'implémentation d'un nouveau type Variant, il sera donc possible de décrire, en plus des opérateurs, les méthodes, fonctions ou propriétés accessible sur ce nouveau type. Le type se comportera alors comme une classe !

      Conclusion
      Les possibilités offertes par Delphi 6 pour définir un nouveau type sont extrêmement importantes. Elles permettent de se rapprocher des possibilités du C++ où la surcharge d'opérateur existe depuis bien longtemps sur les classes.

    2. Implémentation dans Delphi 6

      1. Nouvelles classes de Delphi 6

        L'implémentation dans Delphi d'un nouveau type fait intervenir les classes suivantes :

        TCustomVariantType
        C'est la classe de base pour définir un nouveau type.
        Tous les mécanismes de copie, d'initialisation, et de transtypage sont déclarées sous forme de méthodes virtuelles, voire abstraites.
        De même, les opérateurs unaires, binaires et de comparaison apparaissent sous forme de méthodes.

        La création d'un objet de cette classe conduit à un recensement du nouveau type dans la liste LVarTypeSync. Cette liste sera utilisée dans toutes les méthodes _VarXXX pour traiter les nouveaux types.

        TInvokeableVariantType
        Cette classe implémente les Variants avec méthodes et propriétés.

        TPublishableVariantType
        Cette classe implémente les Variants qui peuvent être stockés sous forme de classe en rendant transparent l'accès des propriétés PUBLIEES de cet objet.

        On utilisera une des trois classes suivant le nouveau type de Variant que l'on souhaite définir.

      2. Exemple fourni avec Delphi 6 : Les nombres complexes

        L'unité VarCmplx fournie l'implémentation des nombres complexes sous forme de Variant.

        Ainsi le code suivant devient valide :

        var
        FA, FB: Variant;
        begin
        FA := VarComplexCreate (1, 2);
        FB := VarComplexCreate ('4+0.5i');
        ShowMessage (FA+FB);
        end;

        Avec cette implémentation, les nombres complexes deviennent des types comme les autres. L'opérateur + est par exemple autorisé et le transtypage en chaîne est automatique.

        Cette implémentation fournie une très bonne base de départ pour implémenter ses propres types.

  4. EXEMPLE D'UN VARIANT "TStringList"

    Avec notre variant, le code suivant devra être valide :

    var
    FA, FB, FC: Variant ;
    b: Boolean ;
    begin
    FA := VarStringListCreate ; // Création d'un variant FA
    FA.Duplicates := True ;
    FA := FA + 1 ; // Ajout de la chaîne '1' à la liste FA
    FA := FA + '5' ; // Ajout de la chaîne '5' à la liste FA
    FB := VarStringListCreate ;
    FB := FA ; // Recopie de FA dans FB
    FB := FB - 1 ; // Suppression de la chaîne '1' dans FB
    b := FA < FB ; // Comparaison de deux listes
    ShowMessage (FA+FB); // Concaténation des listes et affichage sous forme de chaîne
    end;

    L'objectif de ce paragraphe est donc de définir la classe TStringListVariantType qui va prendre en charge notre nouveau variant.
    Comme le contenu de ce nouveau variant sera stocké une TStringList, on définira la classe TStringListVariantType à partir de la classe TPublishableVariantType.

    Dans le cadre de cette présentation, l'exemple qui sera donné se limitera à quelques fonctionnalités.

    Fonctionnalités attendues Implémentation
    Toutes les propriétés publiques d'un TStringList seront accessibles. Définition de la classe TVStringList.
    L'opérateur + (resp. -) permettra d'ajouter (resp. de supprimer) un élément ou une liste. Surcharde des méthodes BinaryOp (pour réaliser l'opération) et RightPromotion (pour vérifier l'opérande à droite de l'opérateur).
    Les opérateurs de comparaison (<, <=, = >=, >) permettront de comparer la longueur de deux listes. Surcharge de la méthode CompareOp.
    Transtypage en chaîne. Surcharge de la méhodeCastTo (utilisation de la propriété CommaText).
    Copie entre listes Surcharge de la méthode Clear.
    Suppression, tests Surcharge des méthodes Clear et IsClear.
    Stockage du variant

    Définition d'un structure TStringListVarData de type TVarData :

    TStringListVarData = packed record
    VType: TVarType;
    Reserved1, Reserved2, Reserved3: Word;
    VStringList: TVStringList;
    Reserved4: LongInt;
    end;

    Création d'un variant liste vide. Création de la fonction VarStringListCreate.

    L'exemple complet est donné dans le fichier uVStringList.pas.

    Pour être complet, il faudrait surcharger la méthode LeftPromotion pour valider les opérandes à gauche, StreamIn et StreamOut pour pouvoir lire et écrire le variant depuis un flux, etc...

    Bon amusement...

Laurent Pelou