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 :

Tableau Dynamique pointant sur Tableau Statiques


Sujet :

Langage Delphi

  1. #1
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 534
    Points : 25 082
    Points
    25 082
    Par défaut Tableau Dynamique pointant sur Tableau Statiques
    Pour le plaisir de ne pas utiliser Copy dans un code que j'ai fait ce matin (je fais une DLL pour un partenaire, où j'utilise volontairement le moins de lib que possible, donc je réinvente de deux-trois trucs que j'ai déjà écrit comme une persistance objet mais en version ultra ligth), j'ai bidouillé un truc, et j'aimerais savoir ce que vous en pensez et aussi éclairer ma lanterne ...

    j'ai plusieurs tableau constants qui ont cette forme, ils ont juste des tailles différentes ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    const
      PROPERTIES_MAPPING_xxx: array[0..17] of TPropertyMapping =
        (
          (PropertyName: 'p1'; FieldName: 'f1';        ),
          (PropertyName: 'p2'; FieldName: 'f2';     ),
       ...
       );
    j'ai un objet ancêtre à mes objets, qui mappent des propriétés publiés et des champs dans une table, j'ai donc

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
      TSearchObject = class(TPersistent)
      private
        FPropertiesMapping: array of TPropertyMapping;
      protected
        procedure LoadFromDataSet(ADataSet: TDataSet);
    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
    procedure TSearchObject.LoadFromDataSet(ADataSet: TDataSet);
    var
      I: Integer;
      Field: TField;
      PropertyValue: Variant;
    begin
      for I := Low(FPropertiesMapping) to High(FPropertiesMapping) do
      begin
        Field := ADataSet.FindField(FPropertiesMapping[I].FieldName);
        if Assigned(Field) then
        begin
          if Field.IsNull then
          begin
            PropertyValue := Null;
          end
          else
          begin
            if Field.DataType = ftDate then
              PropertyValue := Field.AsDateTime
            else
              PropertyValue := Field.AsString;
          end;
          TEpcRTTIWrapper.SetPropertyValue(Self, FPropertiesMapping[I].PropertyName, PropertyValue);
        end
        else
          raise ENeoScopeLADAPIError.Create(ERR_SEARCH_INVALID_DB_MAPPING);
      end;
    end;
    Vous noterez donc que l'ancêtre utilise FPropertiesMapping, le tableau dynamique, donc voilà, c'est juste que ce tableau n'est initialisé uniquement (et obligatoirement) par les descendants de TSearchObject ... dans une surcharge du constructeur ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    constructor THeritedObject.Create(APointer: Pointer);
    begin
      ....
     
      Pointer(FPropertiesMapping) := @PROPERTIES_MAPPING_HERITED;
      PInteger(Integer(FPropertiesMapping) - 4)^ := Length(PROPERTIES_MAPPING_HERITED);
    end;
    dans l'ancêtre, évidemment, je nettoye mon tableau dynamique bidonné, sinon gare au dégat

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    destructor TSearchObject.Destroy();
    begin
      PInteger(Integer(FPropertiesMapping) - 4)^ := 0;
      FPropertiesMapping := nil;
     
      ...
    end;
    Alors voici enfin ma question, si je mets que l'affectation à nil (sans la modif de la longueur), et bien, pas de violation d'accès dans l'immédiat mais un comportement assez ératique de mon programme par la suite (le Connect de mon TMyConnection fait une violation d'accès, c'est tombé sur lui va savoir pourquoi ...)

    Si je met que l'affectation de la longueur (sans le nil), ben cela semble fonctionner,

    si je mets les deux, cela semble aussi fonctionner, et ça me rassure même si je ne sais pas si cela fait grand chose ...

    Alors est-ce que l'affectation à nil est utile ou pas ? ou pire était dangereuse puisque cela appele DynArrayClear ?

    EDIT : autre question sur une éventulle fuite mémoire, en théorie FPropertiesMapping celui créé par l'instanciation de l'objet n'est qu'une coquille vide ? il n'a pas d'élément donc ne pointe sur aucune zone mémoire, la seule mémoire consommée est le compteur de référence et la longueur du tableau, et le pointeur lui même bien sur ... donc normalement c'est libéré ! non ?
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  2. #2
    Expert éminent sénior

    Avatar de sjrd
    Homme Profil pro
    Directeur de projet
    Inscrit en
    Juin 2004
    Messages
    4 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2004
    Messages : 4 517
    Points : 10 154
    Points
    10 154
    Par défaut
    Aaaaaaarrrrrgggggghhhh !!!!
    Désolé mais là, c'est vraiment affreux

    En plus, il n'y a aucun moyen de faire ça sans risque. A cause du compteur de références.

    Déjà, quand tu fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    constructor THeritedObject.Create(APointer: Pointer);
    begin
      ....
     
      Pointer(FPropertiesMapping) := @PROPERTIES_MAPPING_HERITED;
      PInteger(Integer(FPropertiesMapping) - 4)^ := Length(PROPERTIES_MAPPING_HERITED);
    end;
    Où sont censés être les 4 octets pour la longueur ? Ils sont avant le tableau, certes, mais cet endroit n'est pas réservé ! D'ailleurs, les 4 octets encore en-dessous, pour le comptage de références, ne sont pas réservés non plus.

    Et même s'ils étaient réservés, ils se trouvent dans les sections "constantes", donc c'est casse-gueule : suffit qu'elles soient interdites en écriture et c'est l'AV.

    La longueur, encore, tu pourrais la déclarer et renseigner constante. Ca fonctionnerait. Mais le compteur de références, il est obligé de changer durant l'exécution, donc il n'a pas le droit d'être dans une section constante.

    Et comme les tableaux dynamiques n'ont pas de sentille pour les compteurs de références ne devant pas être modifiés, tu n'y arriveras pas. (les string, elles, ont cette sentinelle : si le RefCounter = -1, alors il n'est jamais modifié, et la chaîne n'est jamais libérée - c'est utilisé par les chaînes constantes, justement ).

    Conclusion, euh : retourne à la bonne vieille recopie de ton tableau Ca peut être fait dans le constructeur de l'ancêtre avec un paramètre de type tableau ouvert.
    sjrd, ancien rédacteur/modérateur Delphi.
    Auteur de Scala.js, le compilateur de Scala vers JavaScript, et directeur technique du Scala Center à l'EPFL.
    Découvrez Mes tutoriels.

  3. #3
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 534
    Points : 25 082
    Points
    25 082
    Par défaut
    Donc, en fait, le fait que cela fonctionne c'est juste de la chance, tu peux qu'en fait, au moment, où j'utilise le tableau, la structure interne du tableau (appelons là TArrayRec à la façon de TStrRec de SysUtils) n'est pas alloué, et donc en fait, j'écrit n'importe où dans mon objet ... je te fais confiance vu tes fonctions sur la sérialisation de tableau ...

    bon donc un Copy avec paramètres
    J'avais commencé par un Copy, mais comme il m'a envoyé baladé, je me suis tient, faisons un essai ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      FPropertiesMapping := Copy(PROPERTIES_MAPPING_xxx, Low(PROPERTIES_MAPPING_xxx), High(PROPERTIES_MAPPING_xxx));
    Cela ne veut pas, c'est con, non, faut que je passe par un SetLength puis CopyMemory ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      SetLength(FPropertiesMapping, Length(PROPERTIES_MAPPING_xxx));
      CopyMemory(@FPropertiesMapping[0], @PROPERTIES_MAPPING_xxx[0], SizeOf(PROPERTIES_MAPPING_xxx));
    Cela ne copie finalement que les pointeurs, ce n'est pas si couteux que cela ... c'est moins fun !

    le SUB EAX, 8 dans _DynArrayClear, semble confirmer que j'écrit ailleurs que dans le tableau ...

    EDIT

    Citation Envoyé par sjrd Voir le message
    Et même s'ils étaient réservés, ils se trouvent dans les sections "constantes", donc c'est casse-gueule : suffit qu'elles soient interdites en écriture et c'est l'AV..
    lol, je ne l'avais pas vu celle là (le fait que j'écrit en fait -4 devant ma constance), si j'avais fait, mais est-ce vraiement constant ? c'est une constante typée donc en fait en Delphi, ça ne donne pas une variable, que l'on peut modifier ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      PInteger(Integer(FPropertiesMapping) - 4)^ := Length(PROPERTIES_MAPPING_PATIENT);
      Pointer(FPropertiesMapping) := @PROPERTIES_MAPPING_PATIENT;
    la j'ai clairement une erreur, j'aurais compris que le tableau contenait bien nil et donc pas de mémoire pour -4 et -8 ...

    Citation Envoyé par sjrd Voir le message
    Conclusion, euh : retourne à la bonne vieille recopie de ton tableau Ca peut être fait dans le constructeur de l'ancêtre avec un paramètre de type tableau ouvert.
    Effectivement, je pourrais redéfinir un autre constructeur, mais j'ai pas envie, donc je le code spécifiquement dans les constructeurs des hérités (j'en ai pas beaucoup)

    EDIT 2 : J'ai testé le coup de la variable juste avant

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    const
      PROPERTIES_MAPPING_xxx_REFCOUNT: Cardinal = 0;
      PROPERTIES_MAPPING_xxx_LENGTH: Cardinal = 0;
      PROPERTIES_MAPPING_xxx: array[0..7] of TPropertyMapping =
    Yep, ça écrit bien dedans, lol, vu mon code, le compteur de Ref, ne peut pas bouger, j'utilise le membre privée sans le passer en paramètre, normalement ça ne bouge pas, d'ailleurs, j'ai eu beau faire des essais, je n'ai jamais réussi à voir un RefCount monté dans une string, si tu as un code pour que je puisse visualiser le phénomène cela m'interesse ...
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  4. #4
    Expert éminent sénior

    Avatar de sjrd
    Homme Profil pro
    Directeur de projet
    Inscrit en
    Juin 2004
    Messages
    4 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2004
    Messages : 4 517
    Points : 10 154
    Points
    10 154
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Donc, en fait, le fait que cela fonctionne c'est juste de la chance, tu peux qu'en fait, au moment, où j'utilise le tableau, la structure interne du tableau (appelons là TArrayRec à la façon de TStrRec de SysUtils) n'est pas alloué, et donc en fait, j'écrit n'importe où dans mon objet ...
    Voilà c'est ça. En fait, plus exactement, tu écris n'importe où dans la mémoire
    Citation Envoyé par ShaiLeTroll Voir le message
    bon donc un Copy avec paramètres
    J'avais commencé par un Copy, mais comme il m'a envoyé baladé, je me suis tient, faisons un essai ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      FPropertiesMapping := Copy(PROPERTIES_MAPPING_xxx, Low(PROPERTIES_MAPPING_xxx), High(PROPERTIES_MAPPING_xxx));
    Cela ne veut pas, c'est con, non, faut que je passe par un SetLength puis CopyMemory ?
    Oui je crois que tu es obligé d'utiliser le SetLength + CopyMemory. Copy ne fonctionne qu'avec les tableaux dynamiques.
    Citation Envoyé par ShaiLeTroll Voir le message
    lol, je ne l'avais pas vu celle là (le fait que j'écrit en fait -4 devant ma constance), si j'avais fait, mais est-ce vraiement constant ? c'est une constante typée donc en fait en Delphi, ça ne donne pas une variable, que l'on peut modifier ?
    Sauf option de compilation, je crois que les constantes globales ne peuvent pas être modifiées. Mais quand elles sont typées elles ont effectivement un emplacement en mémoire, contrairement aux constantes non typées qui sont placées comme valeur immédiates.
    Citation Envoyé par ShaiLeTroll Voir le message
    Effectivement, je pourrais redéfinir un autre constructeur, mais j'ai pas envie, donc je le code spécifiquement dans les constructeurs des hérités (j'en ai pas beaucoup)
    Pourtant c'est nettement plus simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    constructor TAncetre.Create(const PropMappings: array of TPropertiesMapping);
    var
      I: Integer;
    begin
      SetLength(FPropertiesMappings, Length(PropMappings));
      for I := 0 to High(PropMappings) do
        FPropertiesMappings[i] := PropMappings[i];
    end;
     
    constructor THeritedObject.Create(APointer: Pointer);
    begin
      inherited Create(PROPERTIES_MAPPING_HERITED);
      ....
    end;
    Fais quand même gaffe au CopyMemory ! Tes TPropertiesMapping contiennent des données dynamiques (chaînes de caractères). C'est nettement plus sûr de faire une copie membre à membre des record.
    Citation Envoyé par ShaiLeTroll Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    const
      PROPERTIES_MAPPING_xxx_REFCOUNT: Cardinal = 0;
      PROPERTIES_MAPPING_xxx_LENGTH: Cardinal = 0;
      PROPERTIES_MAPPING_xxx: array[0..7] of TPropertyMapping =
    Tu aurais au moins pu les mettre dans un record : ça t'aurait assuré que les emplacements mémoire restent là où ils doivent être.
    Citation Envoyé par ShaiLeTroll Voir le message
    Yep, ça écrit bien dedans, lol, vu mon code, le compteur de Ref, ne peut pas bouger, j'utilise le membre privée sans le passer en paramètre, normalement ça ne bouge pas, d'ailleurs, j'ai eu beau faire des essais, je n'ai jamais réussi à voir un RefCount monté dans une string, si tu as un code pour que je puisse visualiser le phénomène cela m'interesse ...
    Rien de plus simple :
    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
    var
      Str, Str2: string;
    begin
      // Pointer(Str) = Pointer(Str2) = nil
      Str := 'Some string';
      UniqueString(Str);
      // Pointer(Str) = @'S' - pardon pour l'abus de langage ^^
      // RefCount(Str) = 1
      Str2 := Str;
      // Pointer(Str) = Pointer(Str2) = @'S'
      // RefCount(Str) = RefCount(Str2) = 2
      WriteLn('RefCount: ', PInteger(Integer(Pointer(Str)) - 8)^;
      Str2 := 'Other string';
      UniqueString(Str2);
      // Pointer(Str2) = 'O'
      // RefCount(Str) = 1 ; RefCount(Str2) = 1
      Str := 'Yet another string';
      // RefCount('Some string') = 0 -> elle est libérée
    end;
    sjrd, ancien rédacteur/modérateur Delphi.
    Auteur de Scala.js, le compilateur de Scala vers JavaScript, et directeur technique du Scala Center à l'EPFL.
    Découvrez Mes tutoriels.

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 534
    Points : 25 082
    Points
    25 082
    Par défaut
    Merci pour le code,
    si local = -1
    si global = inc
    si membre = inc
    très intéressant, cela explique pourquoi, je ne l'avais jamais vu, n'ayant eu besoin de comprendre ce fonctionnement que sur des chaines locales (un comble ! non ?)
    Le coup du pointeur sur chaine dans un Data d'un TreeView par exemple, je comprends maintenant pourquoi chez certains cela fonctionnait et pas chez moi, c'est l'emplacement de la variable ..., bon étant tout de même courageux, je passé soit par un PString, PChar, un record plus complet ou un objet ...

    Pour le CopyMemory, vu ce que j'avais pondu comme truc, c'est déjà moins pire , et comme c'est constant, cela me va très bien qu'il ne recopie QUE les pointeurs sur les chaines, et n'ont pas toutes les chaines, ... car le CountRef d'une constante reste à -1 donc à chaque fois, il copie la chaine, et même si pour ce projet ce n'est pas grave, je pense à un vieux projets auxquels j'ai participé, et plus j'y pense, et plus je me dit que l'on consommait une quantité mémoire inutile à cause des duplications de chaines à tout va ...

    Pour le constructeur, en fait, j'adore, j'ai le constructeur Virtual; abstract; en public qui est surchargé, et le constructeur avec tableau en protected pour être utiliser dans les surcharges, Excellent en fait ! Merci !
    Et le passage de paramètre fixe\ouvert c'est effectivement sympa !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  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
    hello,

    j'ai pas tout lu, mais si j'ai bien compris ton problème, le plus simple dans ce cas est de passer par un pointeur

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    type
     PPropertyMapping=^TPropertyMapping;
     
    constructor THeritedObject.Create(APointer: Pointer);
    begin
      ....
     
      FProperties { : PPropertyMapping  } := @PROPERTIES_MAPPING_HERITED[0];
      FPropertyCount := Length(PROPERTIES_MAPPING_HERITED);
    end;
    c'est suffisant si tu ne veux pas accéder aux propriétés par index
    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
     
    procedure TSearchObject.LoadFromDataSet(ADataSet: TDataSet);
    var
      I: Integer;
      Field: TField;
      PropertyValue: Variant;
      Prop: PPropertyMapping;
    begin
      Prop:=FProperties;
      for I := 0 to FPropertyCount-1 do
      begin
        Field := ADataSet.FindField(Prop.FieldName);
        if Assigned(Field) then
        begin
          if Field.IsNull then
          begin
            PropertyValue := Null;
          end
          else
          begin
            if Field.DataType = ftDate then
              PropertyValue := Field.AsDateTime
            else
              PropertyValue := Field.AsString;
          end;
          TEpcRTTIWrapper.SetPropertyValue(Self, Prop.PropertyName, PropertyValue);
        end
        else
          raise ENeoScopeLADAPIError.Create(ERR_SEARCH_INVALID_DB_MAPPING);
       inc(Prop); // propriété suivante
      end;
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 534
    Points : 25 082
    Points
    25 082
    Par défaut
    Merci Paul, effectivement, j'avais pensé à la méthode du pointeur, comme je suis en train de faire une DLL compatible C++, c'est une syntaxe que j'avais pensé, mais juste que je voulais justement bénéficier de l'index comme dans un tableau ... hum, maintenant, j'avoue que le pointeur m'évite un CopyMemory, donc économise de la mémoire, cela m'évite de me poser des question sur le DynArrayClear de mon tableau dynamique (qui pointe sur ma constante, sans le CountRef à -1, ça ferait KaBoom)

    Donc ça me plait bien ... allez, je vais pondre cette version puisque toute manipulation du tableau est interne (et pas longue)

    EDIT : Le Résultat, simple et efficace

    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
    type
      PPropertyMapping = ^TPropertyMapping;
      TPropertiesMapping = record
        Items: PPropertyMapping;
        Count: Integer;
      end;
      TPropertyMapping = record
        PropertyName: string;
        FieldName: string;
      end;
     
      TSearchObject = class(TPersistent)
      private
        FPropertiesMapping: TPropertiesMapping;
      protected
        ...
        procedure LoadFromDataSet(ADataSet: TDataSet);
        function PropertyToFieldName(const APropertyName: string): string;
        constructor Create(const APropertiesMapping: array of TPropertyMapping); overload;
      public
        constructor Create(APointer: Pointer); overload; virtual; abstract;
        destructor Destroy(); override;
        function ToStruct: Pointer; virtual; abstract;
        class procedure FreeRecord(APointer: Pointer); virtual; abstract;
        ...
      end;
      TSearchObjectClass = class of TSearchObject;
     
      THeritedObject = class(TSearchObject)
      private
       ...
      public
        constructor Create(APointer: Pointer); override;
        function ToStruct: Pointer; override;
        class procedure FreeRecord(APointer: Pointer); override;
      published
        property ...
      end;
    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
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
     
    constructor TSearchObject.Create(const APropertiesMapping: array of TPropertyMapping);
    begin
      inherited Create();
     
      FPropertiesMapping.Items := @APropertiesMapping[0];
      FPropertiesMapping.Count := Length(APropertiesMapping);
    end;
     
    destructor TSearchObject.Destroy();
    begin
      FPropertiesMapping.Count := 0;
      FPropertiesMapping.Items := nil;
     
      inherited Destroy();
    end;
     
     
    procedure TSearchObject.LoadFromDataSet(ADataSet: TDataSet);
    var
      I: Integer;
      Field: TField;
      PropertyValue: Variant;
      PropertyMapping: PPropertyMapping;
    begin
      PropertyMapping := FPropertiesMapping.Items;
     
      for I := 0 to FPropertiesMapping.Count - 1 do
      begin
        Field := ADataSet.FindField(PropertyMapping^.FieldName);
        if Assigned(Field) then
        begin
          if Field.IsNull then
          begin
            PropertyValue := Null;
          end
          else
          begin
            if Field.DataType = ftDate then
              PropertyValue := Field.AsDateTime
            else
              PropertyValue := Field.AsString;
          end;
          TEpcRTTIWrapper.SetPropertyValue(Self, PropertyMapping^.PropertyName, PropertyValue);
        end
        else
          raise ENeoScopeLADAPIError.Create(ERR_SEARCH_INVALID_DB_MAPPING);
     
        Inc(PropertyMapping); // Next
      end;
    end;
     
    function TSearchObject.PropertyToFieldName(const APropertyName: string): string;
    var
      I: Integer;
      PropertyMapping: PPropertyMapping;
    begin
      PropertyMapping := FPropertiesMapping.Items;
     
      for I := 0 to FPropertiesMapping.Count - 1 do
      begin
        if SameText(APropertyName, PropertyMapping^.PropertyName) then
        begin
          Result := PropertyMapping^.FieldName;
          Exit;
        end;
     
        Inc(PropertyMapping); // Next
      end;
     
      raise ENeoScopeLADAPIError.Create(ERR_SEARCH_INVALID_DB_MAPPING);
    end;
     
     
     
     
    const
      PROPERTIES_MAPPING_HERITED: array[0..17] of TPropertyMapping =
        (
          (PropertyName: 'p1'; FieldName: 'f1';     ),
          (PropertyName: 'p2'; FieldName: 'f2';     ),
          ...
        );
     
    constructor THeritedObject.Create(APointer: Pointer);
    begin
      Create(PROPERTIES_MAPPING_HERITED);
     
      if Assigned(APointer) then
        with PHerited(APointer)^ do
        begin
          ...
        end;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  8. #8
    Membre éprouvé
    Avatar de Montor
    Homme Profil pro
    Autre
    Inscrit en
    Avril 2008
    Messages
    879
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Avril 2008
    Messages : 879
    Points : 963
    Points
    963
    Par défaut
    je pense que la reponse est simple ...il y a beaucoup de gent qui pense ainsi
    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
    const PROPERTIES_MAPPING :record
          Ref,Len:integer;
          Data:array[0..17] of TPropertyMapping
          end =(Ref:0;Len:Length(PROPERTIES_MAPPING.Data);
                Data :(
                        (PropertyName: 'p1'; FieldName: 'f1';),
                        (PropertyName: 'p2'; FieldName: 'f2';),
                       ...
                       )
                );
     
    constructor THeritedObject.Create(APointer: Pointer);
    begin
      ....
      Pointer(FPropertiesMapping) := @PROPERTIES_MAPPING.Data;
     
    end;
     
    destructor TSearchObject.Destroy();
    begin
      Pointer(FPropertiesMapping)   := nil;
     
      PPointer(@FPropertiesMapping)^:= nil;
     // un appel explicite,le cast pourrait declencher une routine magic
     // FPropertiesMapping := nil;
      ...
    end;

  9. #9
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 534
    Points : 25 082
    Points
    25 082
    Par défaut
    Merci Montor de revenir sur ce sujet, un et demi plus tard

    C'est quoi cette histoire de Routine Magic, je suis en D7, ... j'ignore de quoi tu fais mention !

    Et pire, tu fais l'erreur d'affecter nil à FPropertiesMapping (un array of TPropertyMapping) comme je l'avais fait à mon 1er essai, qui donc libère la constante !!!

    Tu n'as pas noté dans la solution de Paul Toth d'utiliser un type Pointeur et de l'ajout du [0] qui permet de récupérer l'adresse du 1er Element et non l'adresse du tableau car j'ai utilisé un paramètre tableau ouvert (suggéré par sjrd) et je l'ai placé dans le constructeur de l'ancêtre et non dans chaque héritée contrairement à ce que j'avais prévu à l'origine et ensuite le parcours est géré par pointeur sur les élements sans utiliser les mécanismes du tableau ...
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  10. #10
    Membre éprouvé
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Points : 1 294
    Points
    1 294
    Par défaut
    un truc utile parfois :


    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
    type
      PIntFixArray = ^TIntFixArray;
      TIntFixArray = packed array[0..25] of integer;
     
      PIntArrayPtr = ^TIntArrayPtr;
      TIntArrayPtr = packed array[0..0] of integer;
     
     
    procedure SetIntArray(const Ptr: PIntArrayPtr; const Size: integer);
    var
      I: integer;
    begin
      for I := 0 to Size-1 do
        Ptr[I] := random(1000);
    end;
     
     
     
    procedure TForm22.FormCreate(Sender: TObject);
    var A : TIntFixArray;
        I, L : integer;
        P : PIntArrayPtr;
    begin
      P := @A;
      L := Length(A);
      SetIntArray(P, L);
     
      for I := 0 to L-1 do
        ListBox1.Items.Add(IntToStr(P[I]));
    end;
    donc dans une classe, il suffit d'avoir un pointeur comme PIntArrayPtr mais sur un autre type et de stocker sa taille :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    TMaClass = class
    private
      fPArray : PArrayPropertiesMapping;
      fLength : integer;
    ...
    et on exclus définitivement l'utilisation d'un tableau dynamique.
    cela est comme l'utilisation de ScanLine dans TBitmap.
    une façon un peu plus dans l'esprit du C/C++ de manipuler les tableaux.
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  11. #11
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 534
    Points : 25 082
    Points
    25 082
    Par défaut
    C'est aussi la méthode utilisée en Delphi 3 lorsque les tableaux dynamiques n'existaient pas ... bien connu dans la TStringList ou TList ...
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  12. #12
    Membre éprouvé
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Points : 1 294
    Points
    1 294
    Par défaut
    sincerement c'est assé pratique, je l'utilise dés que possible, de plus, bien plus performant qu'un tableau dynamique au niveau alocation.
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  13. #13
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 534
    Points : 25 082
    Points
    25 082
    Par défaut
    Personnellement, en général, je préfère une TList et des Pointeurs sur Record,
    Le Tableau, je l'utilise surtout en Fixe (et pour des constantes) ensuite des que je dois gérer un ensemble avec une longueur non déterminé à l'avance, je préfère la TList (TStringList ou TObjectList).

    Lorsque j'en arrive à ce genre d'extrémité, je préfère allouer un bloc mémoire et le gérer entièrement par pointeur (PItemFirst et un Inc sur une variable locale comme je l'ai fait pour PPropertyMapping)

    Quoi qu'il arrive toujours dans un contexte locale à une procédure ou alors encaspulé par un objet ... jamais en live, c'est pas un cadeau pour les suivants, personne dans ma boite connaissait cette utilisation

    Les pointeurs et la mémoire c'est le mal, c'est soit disant anti-eXtreme Programming qui veut que le code soit simple et compréhensible au 1er coup d'oeil ... moi ça me dérange pas de mettre 5 lignes de commentaires pour en expliquer une seule ligne de code complexe
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  14. #14
    Membre éprouvé
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Points : 1 294
    Points
    1 294
    Par défaut
    c'est sur, TList est trés pratique la plupart du temps, encore plus avec la TList generique qui permet une derivation rapide pour prendre en charge de nouveaux elements.

    De plus, les pointeur tableaux, tout comme les tableaux en general sont assé complexe à manipuler pour par exemple les echanges de valeur ou la suppression de valeur qui se font à grand coups de move/copymemory.

    un autre avantage des pointeur tableau c'est la possibilité de lire directement un tableau stocké sur le disque via les FileView ou en memoire via un MemoryStream.
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  15. #15
    Membre éprouvé
    Avatar de Montor
    Homme Profil pro
    Autre
    Inscrit en
    Avril 2008
    Messages
    879
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Avril 2008
    Messages : 879
    Points : 963
    Points
    963
    Par défaut
    Citation Envoyé par ShaiLeTroll
    C'est quoi cette histoire de Routine Magic, je suis en D7, ... j'ignore de quoi tu fais mention !
    sur d'autres langages...se sont les routines utiliser en interne et qui sont prefixée toujours par "_" .

    Citation Envoyé par ShaiLeTroll
    Et pire, tu fais l'erreur d'affecter nil à FPropertiesMapping (un array of TPropertyMapping) comme je l'avais fait à mon 1er essai, qui donc libère la constante !!!
    Non peut-etre que tu n'a pas bien compris l'exemple,tu es incapable de comprendre ton propre code
    pour charger les elements tu cité
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Pointer(FPropertiesMapping) := @PROPERTIES_MAPPING_HERITED;
    si tu veux faire un marche arrière tu fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Pointer(FPropertiesMapping) := nil;
    ce n'est pas le meme cas avec (l'erreur que tu as fait)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    FPropertiesMapping:=nil
    ...c'est un truc comme ca
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    PCardinal(@FPropertiesMapping)^:=0;
    avec ce code tu accède directement au FPropertiesMapping sans l'invocation de DynArrayClear()

  16. #16
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 534
    Points : 25 082
    Points
    25 082
    Par défaut
    Ah, tu faisais allusion au PHP par exemple, ce matin, j'ai eu une formation Zend Framework ... je pensais que tu évoquais une nouveaute de Delphi ... il faut avouer que le compilateur est assez mystérieux pour moi à ce sujet, il ajoute plein d'appel non soupçonné ...

    Sinon, je confirme, je suis allé totalement en tatonnant avec cette histoire, d'où l'ouverture de cette discussion, j'étais dans le flou ... d'ailleurs, c'est bien pour cela que que j'ai effectué un retour au pointeur sur les items et non sur le tableau, c'est quelque chose que je maitrise mieux, j'aime bien la TList ou les listes chainées (pour le fun) donc les pointeurs de structures, c'est un basique

    Utiliser le type Pointer pour l'affectation à nil, est effectivement logique, ... je n'y avais pas pensé ... mais oui, j'ai triché pour l'affectation, je devais tricher pour la "dés-affectation" ... Bien Vu !
    Je n'avais pas vu la subtilité, genre de truc à vachement commenté !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Manipulation de pointeur sur tableau dynamique
    Par vincenet dans le forum Débuter
    Réponses: 12
    Dernier message: 09/12/2014, 17h53
  2. [XL-2010] Filtrer tableau dynamique (virtuel) sur VBA
    Par juju152 dans le forum Macros et VBA Excel
    Réponses: 12
    Dernier message: 11/04/2014, 15h33
  3. alternance couleur sur tableau dynamique
    Par asus02 dans le forum Langage
    Réponses: 25
    Dernier message: 13/11/2011, 01h49
  4. [Free Pascal] Redimensionner un tableau dynamique en un tableau statique
    Par tekthoninks dans le forum Free Pascal
    Réponses: 5
    Dernier message: 22/03/2009, 22h38
  5. besoin d'aide sur tableau dynamique
    Par littlesquall dans le forum C
    Réponses: 16
    Dernier message: 02/11/2005, 02h50

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