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

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

[D10] Fonction générique renvoyant un ensemble de valeurs de type énuméré


Sujet :

Langage Delphi

  1. #1
    Membre actif
    Avatar de Eric.H
    Homme Profil pro
    Inscrit en
    Décembre 2004
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2004
    Messages : 220
    Points : 286
    Points
    286
    Par défaut [D10] Fonction générique renvoyant un ensemble de valeurs de type énuméré
    Bonjour

    J'ai une fonction qui a partir de données dans une colonne de TDataset, correspondant a une énumération, retourne un set OF de ladite énumération
    Je cherche a savoir si je peut faire plus simple\propre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    TGenre=(g_Aucun,g_Male,g_Femelle);
    SGenre=set of TGenre
     
    FUNCTION TDataset_Helper.ToSetOf_Colonne<TGenSet>(const xFieldName:String):TGenSet; 
    VAR
      Enumerations:TArray<Byte>;
      une_Enumeration:Byte; 
      Ensemble:Set of 0..255;
    BEGIN
      IF (GetTypeKind(TGenSet)<>tkSet)  THEN
        BEGIN
          raise Exception.Create('ToSetOf_Colonne<TGenSet> : Données générique non gérée');  
          Exit;
        END;
     
      Enumerations:=ToArray_Colonne<Byte>(xFieldName); // retourne un TArray<Byte> contenant les valeurs de l'enumeration dans le champ
     
      Ensemble:=[];
      FOR une_Enumeration in Enumerations
        DO include(Ensemble,une_Enumeration);
     
      Move(Ensemble, Result, SizeOf(TGenSet));
    END;
    Ce qui m’embête c'est le Move(), voir le Ensemble.
    J'ai cherché dans les rtti typinfo s'il y avait une fonction pour rajouter une valeur énumérée tkEnumeration directement dans son tlSet Correspondant.
    Ce serait plus propre d'avoir une fonction delphi, et je suis étonnée qu'elle n'existe pas.

  2. #2
    Membre expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Points : 3 565
    Points
    3 565
    Par défaut
    Ha.... quand ça parle de genre, ça me parle Il manque genre fluide, bigenre, agenre et genre neutre dans ton affaire mais c'est pas grave


    Bon, sérieusement, le move est là pour faire un transtypage assez élégant d'ailleurs, mais l'autre solution que j'entrevois serait celle ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    TGenre=(g_Aucun,g_Male,g_Femelle);
    TGenSet=set of TGenre; //<-- erreur dans ton code spotted "sGenre" au lieu de "TGenSet" (Damned, quand on parle de genre le T est important, j'y tiens ! ;) )
     
    FUNCTION TDataset_Helper.ToSetOf_Colonne<TGenSet>(const xFieldName:String):TGenSet; 
    VAR
      Enumerations:TArray<Byte>;
      une_Enumeration:Byte;
     
    BEGIN
      IF (GetTypeKind(TGenSet)<>tkSet)  THEN
        BEGIN
          raise Exception.Create('ToSetOf_Colonne<TGenSet> : Données générique non gérée');  
          Exit;
        END;
     
      Enumerations:=ToArray_Colonne<Byte>(xFieldName); // retourne un TArray<Byte> contenant les valeurs de l'énumeration dans le champ
     
      result:=[];
      FOR une_Enumeration in Enumerations
        DO include(result, TGenre(une_Enumeration));
     
    END;
    Donc, à retenir, un type byte, on peu le transtyper directement dans le type énuméré que l'on souhaite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var A:Byte;
    var B:Ttype_enum;
     
    begin
       A:=ord(B);
     
     
       B:=Ttype_enum(A);
    end;
    Par contre, attention au "out of range", dans ton champ de base de donnée, tu ne dois trouver que des 0, 1 ou 2 !
    Bidouilleuse Delphi

  3. #3
    Membre expert
    Avatar de e-ric
    Homme Profil pro
    Apprenti chat, bienfaiteur de tritons et autres bestioles
    Inscrit en
    Mars 2002
    Messages
    1 561
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Apprenti chat, bienfaiteur de tritons et autres bestioles

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 561
    Points : 3 951
    Points
    3 951
    Par défaut
    Salut

    Petite intervention de ma part...
    Lire directement la valeur binaire d'un énuméré n'est peut-être pas une bonne idée, la représentation interne du type dépend du compilateur et pourrait amener des difficulté de maintenance à l'avenir.
    J'aurais tendance à passer par un masque de bits (sur par exemple 32 bits), la représentation dans la base des donnée est ainsi maîtrisée et le typage de ton champ de base données devient un simple Integer.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // définir un tableau d'entier indexé par l'énuméré, puissances de 2 successives
    ReprGenre : array[TGenre] of Integer = [1,2,4];
     
    // convertir l'ensemble en un integer
    function Genre2Integer(sg : SGenre): Integer;
    var
       g: TGenre;
    begin
      Result := 0;
      for g in sg do
        Inc(Result, ReprGenre[g]);
    End;
     
    function Integer2Genre(v: Integer): SGenre;
    var
       g: TGenre;
    begin
      Result := [];
      for g in TGenre do
        if (ReprGenre[g] and v) = ReprGenre[g] then
          Include(Result, g); 
    End;
    N'ayant pas la possibilité de tester avec une version récente de Delphi, je fais l'impasse sur le code générique, de toutes façons le code dans cette version est rivé au tableau de constantes.

    Attention : dans cette approche, les puissances de 2 choisies ne doivent pas changer si de nouveaux enumérés sont ajoutés, toute insertion dans le type énuméré doit avoir sa contrepartie dans le tableau de puissance de deux qui doivent être uniques.

    Tchüss

    M E N S . A G I T A T . M O L E M
    Debian 64bit, Lazarus + FPC -> n'oubliez pas de consulter les FAQ Delphi et Pascal ainsi que les cours et tutoriels Delphi et Pascal

    "La théorie, c'est quand on sait tout, mais que rien ne marche. La pratique, c'est quand tout marche, mais qu'on ne sait pas pourquoi. En informatique, la théorie et la pratique sont réunies: rien ne marche et on ne sait pas pourquoi!".
    Mais Emmanuel Kant disait aussi : "La théorie sans la pratique est inutile, la pratique sans la théorie est aveugle."

  4. #4
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 448
    Points
    28 448
    Par défaut
    moi je ne comprend pas ceci : Enumerations:=ToArray_Colonne<Byte>(xFieldName);

    de quel type est le champ ?

    si c'est un entier (champ de bits) il suffit de transtyper AsInteger en TGenSet

    je ne comprend pas non plus l'intervention de e-ric, le type énuméré est parfaitement défini (à la taille de la variable près) Ord(g_Aucun) = 0, Ord(g_Male) = 1, Ord(g_Femelle) = 2.

    et ça sert à quoi un Set of TGenre ? [g_Aucun, g_Male, g_Femelle] ?!
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  5. #5
    Membre expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Points : 3 565
    Points
    3 565
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    et ça sert à quoi un Set of TGenre ? [g_Aucun, g_Male, g_Femelle] ?!
    Bah dans la nature, il n'y a pas que Mâle d'un côté et Femelle de l'autre, les deux à la fois ou aucun des deux ça existe aussi (même si le Pape affirme le contraire). Rien que chez l'être humain, du point de vue biologique il existe même 6 sexes différents, si si, et à titre personnel j'en sais quelque chose et c'est pour ça que je peux me permettre de l'affirmer )

    Je suppose que dans le cas présent, il s'agit soit d'une base de données sur les plantes, soit sur les animaux, je suppose et avec laquelle il doit faire avec la structure qui a déjà été donnée aux différents champs (donc pas possible d'utiliser un type différent pour la base de donnée).


    En passant comme ça, ça sent la mauvaise traduction : Le genre est un comportement social ou identité sexuelle (genre psychologique masculin/féminin). Quand on parle de Mâle ou femelle, on parle donc bien de sexe et non de genre : le mot "gender" en anglais signifie parfois "genre" en effet, mais plus souvent "sexe" en français comme ici, c'est un faux ami !

    PS : les Types génériques <TMonType>, je m'y ferais jamais !
    Bidouilleuse Delphi

  6. #6
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 448
    Points
    28 448
    Par défaut
    Citation Envoyé par LadyWasky Voir le message
    Bah dans la nature, il n'y a pas que Mâle d'un côté et Femelle de l'autre, les deux à la fois ou aucun des deux ça existe aussi (même si le Pape affirme le contraire). Rien que chez l'être humain, du point de vue biologique il existe même 6 sexes différents, si si, et à titre personnel j'en sais quelque chose et c'est pour ça que je peux me permettre de l'affirmer )

    Je suppose que dans le cas présent, il s'agit soit d'une base de données sur les plantes, soit sur les animaux, je suppose et avec laquelle il doit faire avec la structure qui a déjà été donnée aux différents champs (donc pas possible d'utiliser un type différent pour la base de donnée).


    En passant comme ça, ça sent la mauvaise traduction : Le genre est un comportement social ou identité sexuelle (genre psychologique masculin/féminin). Quand on parle de Mâle ou femelle, on parle donc bien de sexe et non de genre : le mot "gender" en anglais signifie parfois "genre" en effet, mais plus souvent "sexe" en français comme ici, c'est un faux ami !

    PS : les Types génériques <TMonType>, je m'y ferais jamais !
    je ne conteste pas la complexité biologique qui ne permet pas toujours de décider du sexe d'un organisme humain ou pas, mais les trois valeurs de cet ensemble ne me semble pas associables pour autant...notamment g_Aucun qui ne suppose pas d'autre valeur possible.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  7. #7
    Membre expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Points : 3 565
    Points
    3 565
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    je ne conteste pas la complexité biologique qui ne permet pas toujours de décider du sexe d'un organisme humain ou pas, mais les trois valeurs de cet ensemble ne me semble pas associables pour autant...notamment g_Aucun qui ne suppose pas d'autre valeur possible.
    Les anges n'ont pas de sexe !!!
    Et certains animaux, rares, non plus :
    http://www.biofutur.com/Se-reproduire-sans-sexe

    Non, sérieusement, un ensemble vide était en effet tout aussi bien...
    Bidouilleuse Delphi

  8. #8
    Membre actif
    Avatar de Eric.H
    Homme Profil pro
    Inscrit en
    Décembre 2004
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2004
    Messages : 220
    Points : 286
    Points
    286
    Par défaut
    Merci à tous, j’essaye de répondre à tous…plus ou moins en vrac
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    TGenre=(g_Aucun,g_Male,g_Femelle);
    SGenre=set of TGenre
    //Ce n’était qu’un exemple pour la démonstration, en réalité :
    TIMPR_Face_Impression = (faci_Erreur,faci_Recto_en_Ligne,faci_Verso_en_Ligne,faci_Recto_hors_Ligne,faci_Verso_hors_Ligne,faci_Recto_Onduleuse)
    Paul / e-ric :
    Comme indiqué, ce qui est stocké dans le champ c’est la valeur ord() des membres de l’énumération TGenre soit 0,1,2,3 ce n’est pas la valeur encodée en binaire (ce que l’on fait pour d’autres multiples cas).
    Le g_Aucun c’est lorsque qu’il n’y a rien dans le champ AsInteger(champ a null)=0, on aurait pu met également g_Erreur ou g_Inconnu.
    Le SGenre=Set of TGenre est utilisé pour le passer a une fonction qui remplit automatiquement une liste déroulante des possibles (configurable par l’user) en rajoutant les énumérations précédemment enregistrées lorsque l’on est en modification.

    LadyWasky :
    SGenre n’est pas une erreur, c’est notre norme interne Sx pour les set of d’un Tx
    C’est une fonction générique j’aurais pu ou du l’écrire ToSetOf_Colonne<T>
    Donc dans le code de cette fonction générique je ne peux pas utiliser « var B:TGenre »

    Et oui j’ai fait une confusion entre Genre et Gender, j’aurais dû parler de Sexe, à ne pas confondre avec l’orientation, et rajouter s_inter

    Donc je continu mes recherches, je vais aller fouiller dans les sources de Delphi, voir comment ils font. Car la move m’embête vraiment, même si une_Enumeration:Byte; Ensemble:Set of 0..255; Doit correspondre avec ce que fait Delphi.

  9. #9
    Membre expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Points : 3 565
    Points
    3 565
    Par défaut
    SGenre n’est pas une erreur, c’est notre norme interne Sx pour les set of d’un Tx
    C’est une fonction générique j’aurais pu ou du l’écrire ToSetOf_Colonne<T>
    OK, d'accord, j'ai compris, ça change rien, hop transtypage, direct :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    Type
    TGenre=(g_Aucun,g_Male,g_Femelle,g_intersexe);
    SGenre=set of TGenre;
     
    var Genres:SGenre;
     
    FUNCTION TDataset_Helper.ToSetOf_Colonne<TGenSet>(const xFieldName:String):TGenSet; 
    VAR
      Enumerations:TArray<Byte>;
      une_Enumeration:Byte;
     
    BEGIN
      IF (GetTypeKind(TGenSet)<>tkSet)  THEN
        BEGIN
          raise Exception.Create('ToSetOf_Colonne<TGenSet> : Données générique non gérée');  
          Exit;
        END;
     
      Enumerations:=ToArray_Colonne<Byte>(xFieldName); // retourne un TArray<Byte> contenant les valeurs de l'énumeration dans le champ
     
      result:=[];
      FOR une_Enumeration in Enumerations
        DO include(result, TGenSet(une_Enumeration)); //<-- transtypage direct
     
    END;
     
     
    SGenre:=ToSetOf_Colonne<SGenre>('Nom du champ');
    Bidouilleuse Delphi

  10. #10
    Membre expert
    Avatar de e-ric
    Homme Profil pro
    Apprenti chat, bienfaiteur de tritons et autres bestioles
    Inscrit en
    Mars 2002
    Messages
    1 561
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Apprenti chat, bienfaiteur de tritons et autres bestioles

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 561
    Points : 3 951
    Points
    3 951
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    si c'est un entier (champ de bits) il suffit de transtyper AsInteger en TGenSet
    je ne comprend pas non plus l'intervention de e-ric, le type énuméré est parfaitement défini (à la taille de la variable près) Ord(g_Aucun) = 0, Ord(g_Male) = 1, Ord(g_Femelle) = 2.
    Je pense que ce n'est pas prudent si le type énuméré évolue (insertion d'un nouvel identificateur entre deux plus anciens, suppression d'un identificateur), la méthode que je propose oblige à faire un choix sur les valeurs associées aux énumérés, le développeur prend ses responsabilités.

    Citation Envoyé par Eric.H
    Comme indiqué, ce qui est stocké dans le champ c’est la valeur ord() des membres de l’énumération TGenre soit 0,1,2,3 ce n’est pas la valeur encodée en binaire (ce que l’on fait pour d’autres multiples cas).
    Le g_Aucun c’est lorsque qu’il n’y a rien dans le champ AsInteger(champ a null)=0, on aurait pu met également g_Erreur ou g_Inconnu.
    Le SGenre=Set of TGenre est utilisé pour le passer a une fonction qui remplit automatiquement une liste déroulante des possibles (configurable par l’user) en rajoutant les énumérations précédemment enregistrées lorsque l’on est en modification.
    J'avoue mal comprendre: que veux-tu en définitive stocker?
    - une valeur énumérée ?
    - un ensemble de valeur énumérées ?

    Le g_Aucun ne se justifie pas, l'ensemble vide correspond sémantiquement à Null (aucune valeur), ma fonction Genre2Integer retourne alors 0 pour un tel cas, aucun bit n'est levé.

    Autre remarque :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    // retourne un TArray<Byte> contenant les valeurs de l'énumeration dans le champ
    pourquoi passer par un tableau alors que l'ensemble est itérable ?

    Cdlt

    M E N S . A G I T A T . M O L E M
    Debian 64bit, Lazarus + FPC -> n'oubliez pas de consulter les FAQ Delphi et Pascal ainsi que les cours et tutoriels Delphi et Pascal

    "La théorie, c'est quand on sait tout, mais que rien ne marche. La pratique, c'est quand tout marche, mais qu'on ne sait pas pourquoi. En informatique, la théorie et la pratique sont réunies: rien ne marche et on ne sait pas pourquoi!".
    Mais Emmanuel Kant disait aussi : "La théorie sans la pratique est inutile, la pratique sans la théorie est aveugle."

  11. #11
    Membre actif
    Avatar de Eric.H
    Homme Profil pro
    Inscrit en
    Décembre 2004
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2004
    Messages : 220
    Points : 286
    Points
    286
    Par défaut
    Citation Envoyé par e-ric Voir le message
    Je pense que ce n'est pas prudent si le type énuméré évolue (insertion d'un nouvel identificateur entre deux plus anciens, suppression d'un identificateur), la méthode que je propose oblige à faire un choix sur les valeurs associées aux énumérés, le développeur prend ses responsabilités.
    Ben c'est tous simplement interdit, ta solution est peut être plus secure, mais on perd en visibilité pour par exemple le débug ord(enum) valant 1 mais que l'on stocke en valeur 4...
    Sachant que l'on a plus de 300 ensemble et on mon avis dans les 600 types énumérés.

    Citation Envoyé par e-ric Voir le message
    J'avoue mal comprendre: que veux-tu en définitive stocker?
    - une valeur énumérée ?
    - un ensemble de valeur énumérées ?
    Dans un champ d'une table, en mémoire, sont stockés les valeurs énumérées et on récupère l’ensemble des valeurs pour x lignes

  12. #12
    Membre actif
    Avatar de Eric.H
    Homme Profil pro
    Inscrit en
    Décembre 2004
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2004
    Messages : 220
    Points : 286
    Points
    286
    Par défaut
    Citation Envoyé par LadyWasky Voir le message
    OK, d'accord, j'ai compris, ça change rien, hop transtypage, direct :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    Type
    TGenre=(g_Aucun,g_Male,g_Femelle,g_intersexe);
    SGenre=set of TGenre;
     
    var Genres:SGenre;
     
    FUNCTION TDataset_Helper.ToSetOf_Colonne<TGenSet>(const xFieldName:String):TGenSet; 
    VAR
      Enumerations:TArray<Byte>;
      une_Enumeration:Byte;
     
    BEGIN
      IF (GetTypeKind(TGenSet)<>tkSet)  THEN
        BEGIN
          raise Exception.Create('ToSetOf_Colonne<TGenSet> : Données générique non gérée');  
          Exit;
        END;
     
      Enumerations:=ToArray_Colonne<Byte>(xFieldName); // retourne un TArray<Byte> contenant les valeurs de l'énumeration dans le champ
     
      result:=[];
      FOR une_Enumeration in Enumerations
        DO include(result, TGenSet(une_Enumeration)); //<-- transtypage direct
     
    END;
     
    SGenre:=ToSetOf_Colonne<SGenre>('Nom du champ');
    Ben j'avais essayé...sur le Result:=[] : [dcc32 Erreur] Global_DB.pas(386): E2010 Types incompatibles : 'TGenSet' et 'Set'.
    et sur l'include : [dcc32 Erreur] Global_DB.pas(388): E2008 Types incompatibles.

    Le compilo semble avoir, pour le moment, des limites pour ces cas là.

  13. #13
    Membre expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Points : 3 565
    Points
    3 565
    Par défaut
    En effet, et sans les types génériques ça marchera mieux.
    Bidouilleuse Delphi

  14. #14
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 745
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 745
    Points : 13 306
    Points
    13 306
    Par défaut
    Je rejoins Eric.H sur le manque d'évolution possible.

    Mais rien n'interdit de forcer les valeur d'un ensemble et même dans le désordre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TGenre=(g_Aucun=0, g_NonDefini=g_Aucun, g_Male=1, g_LadyWasky=3, g_Femelle=2);

  15. #15
    Membre expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Points : 3 565
    Points
    3 565
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Je rejoins Eric.H sur le manque d'évolution possible.

    Mais rien n'interdit de forcer les valeur d'un ensemble et même dans le désordre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TGenre=(g_Aucun=0, g_NonDefini=g_Aucun, g_Male=1, g_LadyWasky=3, g_Femelle=2);
    Chercher l'intruse !

    Sinon, après mure réflexion, il y a quelque chose de TRES génant dans l'utilisation systématique de génériques, surtout avec les ensembles :
    Les génériques concernent tous les types, alors que les ensembles ne concernent que les types ordinaux (char, integer, byte, types énumérés..), ce qui fait qu'il est totalement illusoire de penser que l'on va pouvoir retourner un "Set of quelquechose", avec une fonction déclarée utilisant un type générique.

    La solution serait de créer une classe TSet<T> (qui serait l'équivalent d'un Set Of <T>) par exemple, mais je vous défie de le faire, c'est impossible, pourquoi ? Parceque <T> peut être un string par exemple, et un "Set of String", bin, c'est pas possible (sauf à la plage) !

    Donc en soit, la fonction ToSetOf_Colonne<TGenSet>, est une aberration totale !µ
    Et il ne faut même pas espérer passer par les variant, parce qu'un Set of Variant, c'est tout aussi idiot

    Vous n'avez pas le choix; vous devez utiliser une fonction qui renvoie un sGenre.
    Si vous voulez vraiment une fonction polymorphe, utilisez la directive Override en fin de procedure/fonction, ça marche très bien !

    Bref, ça, ça marche :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    unit Unit1;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Data.DB, Datasnap.DBClient;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        ClientDataSet1: TClientDataSet;
        procedure Button1Click(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
    Type
    TGenre=(g_Aucun,g_Male,g_Femelle,g_intersexe);
    SGenre=set of TGenre;
     
     
    TDataset_Helper = class helper for TDataSet
    public
      Function ToArray_Colonne(const xFieldName:String):TArray<byte>;
      function ToSetOf_Colonne(const xFieldName:String):sGenre;
    end;
     
    Function TDataset_Helper.ToArray_Colonne(const xFieldName:String):TArray<byte>;
    var valeurschamps: Tarray<byte>;
    begin
       valeurschamps  := TArray<byte>.Create(1, 2, 0, 3, 1, 2 ,2);
       result:=valeurschamps;
    end;
     
    function TDataset_Helper.ToSetOf_Colonne(const xFieldName:String):sGenre;
    VAR
      Enumerations:TArray<Byte>;
      une_Enumeration:Byte;
     
    BEGIN
      Enumerations:=ToArray_Colonne(xFieldName); // retourne un TArray<Byte> contenant les valeurs de l'énumeration dans le champ
     
      result:=[];
      FOR une_Enumeration in Enumerations
        DO include(result, TGenre(une_Enumeration)); //<-- transtypage direct
     
    END;
     
     
     
    var Genres:SGenre;
     
    procedure TForm1.Button1Click(Sender: TObject);
    var genreset:SGenre;
        ADataSet:TDataset;
     
    begin
    ADataSet:=TDataSet.Create(self);
    genreset:=ADataSet.ToSetOf_Colonne('Nom du champ');
    ADataSet.Free;
    end;
     
    end.
    Bidouilleuse Delphi

  16. #16
    Membre actif
    Avatar de Eric.H
    Homme Profil pro
    Inscrit en
    Décembre 2004
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2004
    Messages : 220
    Points : 286
    Points
    286
    Par défaut
    Citation Envoyé par LadyWasky Voir le message
    Si vous voulez vraiment une fonction polymorphe, utilisez la directive Override en fin de procedure/fonction, ça marche très bien !
    Ce que je veut justement ne pas faire, on a plus de 300 ensembles qui sont classés dans des unités différentes par "module"

  17. #17
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 448
    Points
    28 448
    Par défaut
    Citation Envoyé par Eric.H Voir le message
    Ce que je veut justement ne pas faire, on a plus de 300 ensembles qui sont classés dans des unités différentes par "module"
    c'est quoi le code de "ToArray_Colonne<Byte>(xFieldName);" ? car je reste persuadé que le problème n'est pas pris dans le bon sens.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  18. #18
    Membre expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 53
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Points : 3 565
    Points
    3 565
    Par défaut
    Bah alors c'est tout bête, mais il faudrait connaître le nombre de valeurs de tes types énumérés, juste qu'on peut s'en sortir QUE si tes types énumérés ont moins de 32 valeurs différentes.

    Pourquoi ?
    Imagines, tu as un type énuméré quelconque TMonTypeEnum=[..,..,..,..,..], quand tu déclares le types ensembles correspondant (sMonType:Set of TMonTypeEnum), ce type ensemble va, selon, avoir plusieurs tailles possibles en mémoire :
    set avec moins de 8 valeurs = 8 bits (byte)
    entre 8 et 16 valeurs = 16 bits (word)
    entre 16 et 32 valeurs = 32 bits (LongWord)
    entre 32 et 64 valeurs = 64 bits (2x LongWord)
    entre 64 et 128 valeurs = 128 bits (4x LongWord)
    entre 128 et 256 valeurs = 256 bits (8x LongWord)

    On va en rester à 32 bits, donc à 32 valeurs maximum pour notre type énuméré pour profiter du fait que l'on peut directement transtyper un entier en ensemble (et vice versa). Pour résumer (j'ai été piquer le code sur un site concurrent et je remercie son auteur) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    type 
      TSetInt = (s1, s2, s3); 
      TSetInts = set of TSetInt; 
    const 
      TMax = [s1..s3]; 
      TMid: TSetInts = [s2]; 
    var 
      T: TSetInts; 
      R: integer; 
    begin 
      T := [s1, s3]; 
     
      R := byte(T);    // pour  1..8  éléments dans l'enum 
      R := Word(T);    //  "    9..16   " 
      R := LongWord(T);//  "   17..32   " 
     
      R := Byte(T);        // variable du type du set : OK 
      R := Byte([s1, s3]); // pas possible si passage direct du set 
      R := Byte(TMax);     // pas possible si constante non typée 
      R := Byte(TMid);     // constante typée : OK 
    end;
    Et dans le cas de sGenre qui est donc l'équivalent d'un byte, on peut aussi transtyper dans le sens inverse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Type
    TGenre=(g_Aucun,g_Male,g_Femelle,g_intersexe);
    SGenre=set of TGenre;
     
    var GenreSet:sGenre;
         B:Byte;
     
    begin
       GenreSet:=[gMale,g_intersexe];
       B:=byte(GenreSet); //égal à 5
       //Dans l'autre sens :
       B:=2;
       GenreSet:=sGenre(B); //égale à [g_Femelle]
    end;


    L'idée c'est, de déclarer le Type depuis la base de donnée, de récupérer un LongWord qui représentera TOUT type d'ensemble (ayant pour base un type énuméré avec au maximum 32 valeurs différentes, cette fonction sera indispensable :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure SetBit(var Value: LongWord; const Bit: byte); Register;
    asm
      BTS [eax],edx
    end;
    Et cela va necessiter l'abandon de ToArray_Colonne<Byte>(xFieldName); en une fonction un peu plus simple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function ToLongWord_Colonne(const xFieldName:String):TLongWord; 
    var ValeurChamp:byte;
    begin
      ....
      ValeurChamp:=VotreDataSet..FieldByName('xFieldName:String').AsInteger; //Attention, la valeur retournée doit être inférieure ou égale à 32 !!!
      setBit(result,ValeurChamp);
      ...
    end;
    Pour d'autres procédures de manipulation de bits, voir ici (ClearBit, TestBit, ToggleBit) :
    http://www.developpez.net/forums/d96...ition-binaire/

    Maintenant, il va falloir à notre disposition,une fonction qui "transforme" ce LongWord en Word ou Byte, selon l'ensemble que l'on souhaite récupérer, attention, c'est chaud !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    function GetOrdValue(Info: PTypeInfo; const SetParam): LongWord ;
    begin
      Result := 0;
     
      case GetTypeData(Info)^.OrdType of
        otSByte, otUByte:
          Result := Byte(SetParam);
        otSWord, otUWord:
          Result := Word(SetParam);
        otSLong, otULong:
          Result := LongWord(SetParam);
      end;
    end;
     
    procedure SetOrdValue(Info: PTypeInfo; var SetParam; Value: LongWord );
    begin
      case GetTypeData(Info)^.OrdType of
        otSByte, otUByte:
          Byte(SetParam) := Value;
        otSWord, otUWord:
          Word(SetParam) := Value;
        otSLong, otULong:
          LongWord(SetParam) := Value;
      end;
    end;
    Enfin, mis ensemble, ça donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    var EnsembleChamp:LongWord;
        GenreSet:sGenre;
     
    begin
        EnsembleChamp:=ToLongWord_Colonne('NomDuChamp');
        SetOrdValue(TypeInfo(GenreSet),GenreSet,EnsembleChamp);  
    end;
    Et ça marche pour tous les ensembles (avec moins de 32 valeurs différentes possibles)
    Bidouilleuse Delphi

  19. #19
    Membre actif
    Avatar de Eric.H
    Homme Profil pro
    Inscrit en
    Décembre 2004
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2004
    Messages : 220
    Points : 286
    Points
    286
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    c'est quoi le code de "ToArray_Colonne<Byte>(xFieldName);" ? car je reste persuadé que le problème n'est pas pris dans le bon sens.
    Une simple boucle sur le dataset pour empiler les données trouvées dans le champ, j'ai un peu épuré
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    FUNCTION TDataset_Helper.ToArray_Colonne<T>(const xFieldName:String):TArray<T>; //MX31
    VAR
      Type_Donnee:TTypeKind;
      FieldType:TFieldType;
      Valeur:TValue;
      Convertisseur:TValue; // On pourrait tous faire en une ligne, mais c'est pour plus de clartée   //MX35
    BEGIN
      Result:=[];
      IF IsEmpty THEN Exit;
     
      Type_Donnee:=GetTypeKind(T); //MX35
      CASE Type_Donnee OF //MX35 GetTypeKind(T) OF
        tkInteger:FieldType:=ftInteger;
        tkChar,tkString,tkWChar,tkLString,tkWString,tkUString:FieldType:=ftString;
        .....
      ELSE
        raise Exception.Create('TDataset_Helper.ToArray_Colonne<T> : Données générique non gérée');   // NE PAS TRADUIRE
        Exit;
      END;
     
      DisableControls;
      First;
      WHILE NOT EOF DO
        BEGIN
          IF NOT FieldByName(xFieldName).IsNull THEN
            BEGIN
              CASE FieldType OF
                ftInteger:Valeur:=FieldByName(xFieldName).AsInteger;
                 .....
              END;
              //Debut MX35
              CASE Type_Donnee OF
                tkEnumeration:
                  BEGIN
                    Convertisseur:=TValue.FromOrdinal(System.TypeInfo(T),Valeur.AsInteger);
                    Result:=Result+[Convertisseur.AsType<T>];
                  END
              ELSE
                Result:=Result+[Valeur.AsType<T>];
              END;
              //Fin MX35
              //MX35 Result:=Result+[Valeur.AsType<T>];
            END;
          Next;
        END;
      EnableControls;
    END;

  20. #20
    Membre actif
    Avatar de Eric.H
    Homme Profil pro
    Inscrit en
    Décembre 2004
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2004
    Messages : 220
    Points : 286
    Points
    286
    Par défaut
    Bonjour

    A moins que vous ayez d'autres idées, je vais mettre "Résolue" cette enfilade. Rien trouvé de plus clean.
    J'ai simplement modifié la variable Ensemble qui pointe directement sur le Result, ce qui me permet de virer le move, c'est peut être un peu mieux
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      Ensemble:TIntegerSet absolute Result;
    En tout cas merci a tous.

Discussions similaires

  1. [C++] Projet module IA générique pour MORPG.
    Par xterminhate dans le forum Projets
    Réponses: 5
    Dernier message: 21/08/2005, 11h22
  2. RTTI
    Par J-Marc dans le forum C++
    Réponses: 10
    Dernier message: 06/08/2005, 10h39
  3. affectation de classes generiques
    Par Mehdi Feki dans le forum C++
    Réponses: 16
    Dernier message: 12/03/2005, 16h05
  4. [Template] Pb d'iterateur generique
    Par Cyber@l dans le forum C++
    Réponses: 2
    Dernier message: 10/03/2005, 20h13
  5. [RTTI] TObject.FieldAdress(Name : String) : Pointer
    Par Clorish dans le forum Langage
    Réponses: 3
    Dernier message: 30/09/2004, 14h41

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo