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 :

Copie de variable de type record comprenant des éléments de type structuré


Sujet :

Langage Delphi

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Février 2007
    Messages
    341
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 341
    Points : 150
    Points
    150
    Par défaut Copie de variable de type record comprenant des éléments de type structuré
    Bonjour,

    Je souhaiterai connaître votre avis et vos conseils concernant une affectation qui me semblait toute simple mais qui me donne du fil à retordre.
    J'ai un code de ce genre :
    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
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
     
    unit Unit1;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages,
      System.SysUtils, System.Variants, System.Classes,
      Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        Label1: TLabel;
        Edit1: TEdit;
        Label2: TLabel;
        Edit2: TEdit;
        Button3: TButton;
        Label3: TLabel;
        Label4: TLabel;
        Edit3: TEdit;
        Edit4: TEdit;
        Button4: TButton;
        Button5: TButton;
        Button6: TButton;
        Button7: TButton;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure Button3Click(Sender: TObject);
        procedure Button4Click(Sender: TObject);
        procedure Button5Click(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
        procedure affedit;
      end;
     
    var
      Form1: TForm1;
     
    type
       t3=record
          s3: string;
          i3: integer;
          j3: integer;
          a3: array of variant;
          end;
     
       t2=record
          s2: string;
          i2: integer;
          a2: array of t3;
          end;
     
       t1=record
          s1: string;
          i1: integer;
          a1: array of t2;
          end;
     
    var
       b, b2: t1;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.affedit;
    begin
    edit1.text:=b.s1;
    edit2.text:=b2.s1;
    edit3.text:=b.a1[0].a2[1].j3.tostring;
    edit4.text:=b2.a1[0].a2[1].j3.tostring;
    end;
     
    // remplissage de la variable b1 avec des valeurs
    procedure TForm1.Button1Click(Sender: TObject);
    var
       i, j, k, x, y, z: integer;
    begin
    // raz b et b2
    b.s1:='';
    b.i1:=0;
    setlength(b.a1, 0);
    b2.s1:='';
    b2.i1:=0;
    setlength(b2.a1, 0);
    edit1.text:='';
    edit2.text:='';
    edit3.text:='';
    edit4.text:='';
    // on peut mettre les valeurs que l'on veut pour x, y et z
    x:=5;
    y:=10;
    z:=15;
    // remplissage de tous les niveaux
    b.s1:='chaine';
    b.i1:=x;
    // en définissant bien la taille des array avant de les remplir
    setlength(b.a1, b.i1);
    for i:=0 to b.i1-1 do
       begin
       b.a1[i].s2:='chaine_i_'+i.tostring;
       b.a1[i].i2:=y;
       setlength(b.a1[i].a2, b.a1[i].i2);
       for j:=0 to b.i1-1 do
          begin
          b.a1[i].a2[j].s3:='chaine_j_'+j.tostring;
          b.a1[i].a2[j].i3:=z;
          b.a1[i].a2[j].j3:=z-3;
          setlength(b.a1[i].a2[j].a3, b.a1[i].a2[j].i3);
          for k:=0 to b.a1[i].a2[j].i3-1 do
             begin
             b.a1[i].a2[j].a3[k]:='chaine_k_'+k.tostring;
             end;
          end;
       end;
    edit1.text:=b.s1;
    edit3.text:=b.a1[0].a2[1].j3.tostring;
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    // affectation des valeurs de b à b2
    b2:=b;
    affedit;
    end;
     
    procedure TForm1.Button3Click(Sender: TObject);
    begin
    // modification valeur de b.s1
    b.s1:='autre nom';
    // pas de répercussion sur b2.s1
    affedit;
    end;
     
    procedure TForm1.Button4Click(Sender: TObject);
    begin
    // modification valeur de b.a1[0].a2[1].j3
    b.a1[0].a2[1].j3:=20;
    // répercussion sur b2.a1[0].a2[1].j3
    affedit;
    end;
     
    procedure TForm1.Button5Click(Sender: TObject);
    var
       i, j, k: integer;
    begin
    b2.s1:=b.s1;
    b2.i1:=b.i1;
    setlength(b2.a1, b.i1);
    for i:=0 to b.i1-1 do
       begin
       b2.a1[i].s2:=b.a1[i].s2;
       b2.a1[i].i2:=b.a1[i].i2;
       setlength(b2.a1[i].a2, b.a1[i].i2);
       for j:=0 to b.a1[i].i2-1 do
          begin
          b2.a1[i].a2[j].s3:=b.a1[i].a2[j].s3;
          b2.a1[i].a2[j].i3:=b.a1[i].a2[j].i3;
          b2.a1[i].a2[j].j3:=b.a1[i].a2[j].j3;
          setlength(b2.a1[i].a2[j].a3, b.a1[i].a2[j].i3);
          for k:=0 to b.a1[i].a2[j].i3-1 do
             b2.a1[i].a2[j].a3[k]:=b.a1[i].a2[j].a3[k];
          end;
       end;
    affedit;
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    Button1Click(sender);
    end;
     
    end.
    Lorsque j'affecte b2:=b (button2) et que je modifie b.s1 en 'autre nom' (button3), il n'y a pas de répercussion sur b2.s1
    Mais si je modifie la valeur de b.a1[0].a2[1].j3 (button4), cette modification se répercute dans b2.a1[0].a2[1].j3, alors que en faisant b2:=b je pensais affecter une nouvelle variable et non un pointeur ?
    On dirait que le array a1 n'est pas copié, mais que c'est uniquement le pointeur sur b.a1 qui est copié dans b2.a1.

    En revanche quand je fait l'affectation des valeurs comprises dans b à b2 par affectation de tous les éléments un par un (button5), la modification de la valeur de b.a1[0].a2[1].j3 (button4), ne se répercute pas dans b2.a1[0].a2[1].j3.
    Y aurait-il une manière plus élégante et surtout plus rapide de de procéder ?

    Merci d'avance de vos réponses et conseils. Bien cordialement

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 451
    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 451
    Points : 24 859
    Points
    24 859
    Par défaut
    C'est vrai que j'aurais pensé aussi qu'il y aurait une copie mais tu utilises des variables globales ... c'est pas justement le cas ou il y a un compteur de référence qui entre en jeu (pour String, c'est sur, les tableaux un doute)
    Faudrait vérifier CopyRecord pour moi ça utilise les informations de type pour copier

    Quand j'y réfléchi, je ne crois jamais avoir des record aussi complexe, je préfère très vite une structure plutôt objet.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ShowMessage(IntToHex(NativeInt(@b.a1[0].a2[1].j3), 16));
    ShowMessage(IntToHex(NativeInt(@b2.a1[0].a2[1].j3), 16));
    Même adresse donc clairement la copie est plus complexe, peux-tu redéfinir class operator := pour faire proprement la copie, je ne sais pas si c'est faisable !
    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

  3. #3
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 669
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 669
    Points : 5 238
    Points
    5 238
    Par défaut
    Les puriste me rectifieront si je dis une bêtise, car j'écris ça de mémoire, mais ce comportement est normal car il est dû au type record et aux types des données stockées dans tes record.

    Lorsque que tu fait b2:=b, puisqu'ils sont de type record cela déclenche une copie.
    Cela déclenche donc quelque chose qui ressemble à ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    b2.s1 := b.s1;
    b2.i1 :=  b.i1;
    b2.a1 := b.a1;
    s1 et i1 sont des types scalaires, l'affectation fait donc une copie de la valeur.
    Ce qui explique pourquoi le changement de b.s1 n'a aucun impact sur b2.

    Mais a1 est un tableau et cette affectation va faire pointer b2.a1 et b.a1 au même endroit.
    Ce qui implique que tout changement dans b.a1 va se répercuter dans b2.a1.

    Dans le code sous le bouton 5, tu procèdes manuellement à la copie des éléments scalaire situés dans tes différents tableau.
    Ces éléments étant des types scalaires, l'affectation fait donc une copie de la valeur.
    Et par conséquent, la modification de l'un ne peux pas entraîner la modification de l'autre.

    Voila pour les explications.
    Maintenant comme décris uniquement le comportement que tu constate et pas celui que tu veux, impossible de te donner un code qui correspond à un besoin que tu n'a pas exprimé.

  4. #4
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 451
    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 451
    Points : 24 859
    Points
    24 859
    Par défaut
    copier un tableau dynamique à deux dimensions ... je note que j'avais oublié ce constat de 2009

    Mes Array dans les tableaux sont de tailles fixes, la copie fonctionne dans ce cas.

    Le Clone d'objet c'est un classique, on peut s'inspirer de la logique Assign et AssignTo du TPersitent, je le fais souvent par RTTI
    D'ailleurs, je dois avoir une classe qui traine pour recopier un tableau en profondeur via RTTI, si je retrouve

    Sinon pour écrire ta copie de façon disons plus lisible à long terme

    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
     
    type
       t3=record
          s3: string;
          i3: integer;
          j3: integer;
          a3: array of variant;
     
          function AssignTo(var ADest: t3): t3;
          end;
     
       t2=record
          s2: string;
          i2: integer;
          a2: array of t3;
     
          function AssignTo(var ADest: t2): t2;
          end;
     
       t1=record
          s1: string;
          i1: integer;
          a1: array of t2;
          function AssignTo(var ADest: t1): t1;
          end;
     
    function t1.AssignTo(var ADest: t1): t1;
    var
      I: Integer;
    begin
      ADest.s1 := Self.s1;
      ADest.i1 := Self.i1;
      SetLength(ADest.a1, Length(Self.a1));
      for I := Low(Self.a1) to High(Self.a1) do
        Self.a1[I].AssignTo(ADest.a1[I])
    end;
     
    { t2 }
     
    function t2.AssignTo(var ADest: t2): t2;
    var
      I: Integer;
    begin
      ADest.s2 := Self.s2;
      ADest.i2 := Self.i2;
      SetLength(ADest.a2, Length(Self.a2));
      for I := Low(Self.a2) to High(Self.a2) do
        Self.a2[I].AssignTo(ADest.a2[I])
    end;
     
    { t3 }
     
    function t3.AssignTo(var ADest: t3): t3;
    begin
      ADest.s3 := Self.s3;
      ADest.i3 := Self.i3;
      ADest.j3 := Self.j3;
      ADest.a3 := Copy(Self.a3);
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    // affectation des valeurs de b à b2
    b.AssignTo(b2);
    affedit;
    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

  5. #5
    Membre habitué
    Profil pro
    Inscrit en
    Février 2007
    Messages
    341
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 341
    Points : 150
    Points
    150
    Par défaut
    Merci à vous.
    @popo :
    Je n'ai pas de besoin particulier car j'avais trouvé une solution par moi-même, mais je voulais savoir si mon raisonnement était juste, ce que tu confirmes, et si une solution plus rapide existe.

    @ShaiLeTroll :
    Je n'avais pas pensé à intégrer une fonction dans le record ! Cela peut effectivement être beaucoup plus intéressant et lisible.
    Merci encore pour ce code très limpide (comme d'habitude avec toi ;o)
    En gros ta solution est la même que la mienne mais plus élégante et "maintenable". En terme d'exécution cela doit revenir à peu près au même je pense.
    J'espérais qu'il y avait une syntaxe magique qui permettait de faire tout d'un coup ;o) ... mais non ...
    J'essaye ta solution et je te tiens au courant.
    Je vais creuser aussi CopyRecord.

    Merci

  6. #6
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 451
    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 451
    Points : 24 859
    Points
    24 859
    Par défaut
    Il existe une méthode avec les RTTI pour copier en profondeur, c'est certains, je l'ai plus vu pour de la sérialisation JSON à base de record plutôt que d'objet.
    Si Paul Toth n'a pas cela ne reserve, je serais surpris

    Après si tu n'es un peu suicidaire niveau mémoire, tu peux comme je le disais inhiber le compteur de référence des tableaux, je ne sais pas comment on le fait proprement, à l'instar de UniqueString ou DynArrayUnique (qui n'est pas récursif)



    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 TForm1.Button2Click(Sender: TObject);
    type
      PDynArrayRec = ^TDynArrayRec;
      TDynArrayRec = packed record
      {$IFDEF CPU64BITS}
        _Padding: Integer; // Make 16 byte align for payload..
      {$ENDIF}
        RefCnt: Integer;
        Length: NativeInt;
      end;
    var
      I: Integer;
    begin
    // affectation des valeurs de b à b2
     
      PDynArrayRec(Pointer(NativeInt(b.a1) - SizeOf(TDynArrayRec))).RefCnt := -1;
      for I := Low(b.a1) to High(b.a1) do
        PDynArrayRec(Pointer(NativeInt(b.a1[I].a2) - SizeOf(TDynArrayRec))).RefCnt := -1;
     
      b2 := b;
     
      PDynArrayRec(Pointer(NativeInt(b.a1) - SizeOf(TDynArrayRec))).RefCnt := 1;
      for I := Low(b.a1) to High(b.a1) do
        PDynArrayRec(Pointer(NativeInt(b.a1[I].a2) - SizeOf(TDynArrayRec))).RefCnt := 1;
     
     
      affedit;
    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

  7. #7
    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 430
    Points
    28 430
    Par défaut
    donc oui, c'est une simple copie, sauf que les strings sont COW (Copy On Write) donc quand tu modifies la chaîne qui est pointée par deux variables, il y a une copie à ce moment là et c'est la version modifiée qui est nouvelle. Les tableaux dynamiques ne sont pas COW, il faut utiliser Copy(x) pour en faire une vraie copie.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  8. #8
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 451
    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 451
    Points : 24 859
    Points
    24 859
    Par défaut
    Dans le même principe en forçant la recopie du tableau le compteur de Ref des tableaux après b2 := b est à 2, il faut dissocier les tableaux de chacun

    Citation Envoyé par navyg Voir le message
    J'espérais qu'il y avait une syntaxe magique qui permettait de faire tout d'un coup ;o) ... mais non ...
    L'appel explicite à DynArrayUnique pourrait te plaire, mais il y a surement une raison pour que DynArrayUnique soit pas facile à utiliser contrairement à UniqueString.

    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
    procedure TForm1.Button2Click(Sender: TObject);
    type
       t2array = array of t2;
       t3array = array of t3;
     
    begin
    // affectation des valeurs de b à b2
     
      b2 := b;
     
      DynArrayUnique(Pointer(b2.a1), TypeInfo(t2array));
      for I := Low(b2.a1) to High(b2.a1) do
        DynArrayUnique(Pointer(b2.a1[I].a2), TypeInfo(t3array));
     
     
    affedit;
    end;

    Citation Envoyé par Paul TOTH Voir le message
    il faut utiliser Copy(x) pour en faire une vraie copie.
    Copy fait juste celle de niveau 0, soit b2.a1 est une copy de b.a1 mais cela conserve les ref sur les a2
    Pas de récursivité de copy sur les tableaux dynamiques (ça fonctionne très bien avec un tableau statique)
    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

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 688
    Points : 13 117
    Points
    13 117
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    peux-tu redéfinir class operator := pour faire proprement la copie, je ne sais pas si c'est faisable !
    Ca l'est dans les dernières versions Delphi : class operator Assign(var Dest: TMyRecord; const [ref] Src: TMyRecord);

  10. #10
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 451
    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 451
    Points : 24 859
    Points
    24 859
    Par défaut
    D'ailleurs le décorateur [ref] n'est pas repris lors du CTRL+MAJ+C et pourtant requis !

    Pour ceux qui ont 10.4 (moi j'ai testé sur 11.2)

    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
    type
       t3=record
          s3: string;
          i3: integer;
          j3: integer;
          a3: array of variant;
          end;
     
       t2=record
          s2: string;
          i2: integer;
          a2: array of t3;
     
          class operator Assign(var Dest: t2; const [ref] Src: t2);
          end;
     
       t1=record
          s1: string;
          i1: integer;
          a1: arrayof t2;
     
          class operator Assign(var Dest: t1; const [ref] Src: t1);
          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
     
    { t1 }
     
    class operator t1.Assign(var Dest: t1; const [ref] Src: t1);
    type
       t2array = array of t2;
    begin
      Dest.s1 := Src.s1;
      Dest.i1 := Src.i1;
      Dest.a1 := Src.a1;
     
      DynArrayUnique(Pointer(Dest.a1), TypeInfo(t2array));
    end;
     
    { t2 }
     
    class operator t2.Assign(var Dest: t2; const [ref] Src: t2);
    type
       t3array = array of t3;
    begin
      Dest.s2 := Src.s2;
      Dest.i2 := Src.i2;
      Dest.a2 := Src.a2;
     
      DynArrayUnique(Pointer(Dest.a2), TypeInfo(t3array));
    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

  11. #11
    Membre habitué
    Profil pro
    Inscrit en
    Février 2007
    Messages
    341
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 341
    Points : 150
    Points
    150
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    D'ailleurs le décorateur [ref] n'est pas repris lors du CTRL+MAJ+C et pourtant requis !

    Pour ceux qui ont 10.4 (moi j'ai testé sur 11.2)

    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
    type
       t3=record
          s3: string;
          i3: integer;
          j3: integer;
          a3: array of variant;
          end;
     
       t2=record
          s2: string;
          i2: integer;
          a2: array of t3;
     
          class operator Assign(var Dest: t2; const [ref] Src: t2);
          end;
     
       t1=record
          s1: string;
          i1: integer;
          a1: arrayof t2;
     
          class operator Assign(var Dest: t1; const [ref] Src: t1);
          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
     
    { t1 }
     
    class operator t1.Assign(var Dest: t1; const [ref] Src: t1);
    type
       t2array = array of t2;
    begin
      Dest.s1 := Src.s1;
      Dest.i1 := Src.i1;
      Dest.a1 := Src.a1;
     
      DynArrayUnique(Pointer(Dest.a1), TypeInfo(t2array));
    end;
     
    { t2 }
     
    class operator t2.Assign(var Dest: t2; const [ref] Src: t2);
    type
       t3array = array of t3;
    begin
      Dest.s2 := Src.s2;
      Dest.i2 := Src.i2;
      Dest.a2 := Src.a2;
     
      DynArrayUnique(Pointer(Dest.a2), TypeInfo(t3array));
    end;
    Ca va trop vite pour moi !
    Le temps de tester un truc, voilà une autre solution qui arrive ... Merci

    Alors avec CopyRecord, ça marche pas ...
    Avec les fonctions incluses dans les record ça marche très bien
    Avec la dernière solution sur les class operator, ça marche bien sauf pour les valeurs qui sont dans a3, qui restent toujours sur le même pointeur que b.a1[x].a2[y].a3
    Si je modifie une valeur dans b.a1[x].a2[y].a3[z], cela change aussi la valeur dans b.a1[x].a2[y].a3[z].

    J'essaye de corriger par moi-même mais j'ai du mal ;o) ... je persiste

  12. #12
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 451
    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 451
    Points : 24 859
    Points
    24 859
    Par défaut
    Ah oui, le test d'affichage est sur j3, qui était encore un niveau au dessus des valeurs dans a3

    Donc il faut encore ajouter un niveau pour gérer la copie de a3
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    { t3 }
     
    class operator t3.Assign(var Dest: t3; const [ref] Src: t3);
    begn
      Dest.s3 := Src.s3;
      Dest.i3 := Src.i3;
      Dest.j3 := Src.j3;
      Dest.a3 := Copy(Src.a3); 
    end;
    et d'ailleurs, comme avec les opérateurs Assign, on peut simplifier le code de t1 et t2

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    { t1 }
     
    class operator t1.Assign(var Dest: t1; const [ref] Src: t1);
    begin
      Dest.s1 := Src.s1;
      Dest.i1 := Src.i1;
      Dest.a1 := Copy(Src.a1);
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    { t2 }
     
    class operator t2.Assign(var Dest: t2; const [ref] Src: t2);
    begin
      Dest.s2 := Src.s2;
      Dest.i2 := Src.i2;
      Dest.a2 := Copy(Src.a2);
    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

  13. #13
    Membre habitué
    Profil pro
    Inscrit en
    Février 2007
    Messages
    341
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 341
    Points : 150
    Points
    150
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Ah oui, le test d'affichage est sur j3, qui était encore un niveau au dessus des valeurs dans a3

    Donc il faut encore ajouter un niveau pour gérer la copie de a3
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    { t3 }
     
    class operator t3.Assign(var Dest: t3; const [ref] Src: t3);
    type
       tvariantarray = array of Variant;
    begn
      Dest.s3 := Src.s3;
      Dest.i3 := Src.i3;
      Dest.j3 := Src.j3;
      (*Dest.a3 := Src.a3;
     
      DynArrayUnique(Pointer(Dest.a3), TypeInfo(tvariantarray));*)
      Dest.a3 := Copy(Src.a3); // devrait suffir
    end;
    Argh !!!! je venais de trouver tout seul et j'allais le poster !
    J'avais fait avec le DynArrayUnique pour lequel j'ai eu toutes les peines du monde à trouver la bonne syntaxe.
    J'ai essayé aussi le copy et ça fonctionne également.
    Lequel est le plus rapide ? Il faudrait regarder ce qu'il y a derrière DynArrayUnique .... en remontant, on tombe sur une instruction _copyarray etc.. donc autant faire le copy.

  14. #14
    Membre habitué
    Profil pro
    Inscrit en
    Février 2007
    Messages
    341
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 341
    Points : 150
    Points
    150
    Par défaut
    Tu as du modifier ton dernier post entre temps et j'ai adopté le copy partout
    Le code est simplissime finalement .... quand on sait comment utiliser la bonne syntaxe !
    Encore une découverte pour moi que le class operator dans un record (je n'ai pas vraiment compris ce que venait faire le Const [ref] mais ça marche c'est le principal ;o)

    Je vais indiquer le sujet comme résolu et je pense qu'il peut en intéresser plus d'un ..

  15. #15
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 451
    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 451
    Points : 24 859
    Points
    24 859
    Par défaut
    le const [Ref] assure qu'il n'y ait pas de copie de la Source, une copie donc une Assignation ... ça ferait sans cela une Récursivité jusqu'au Stack Overflow.
    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

  16. #16
    Membre habitué
    Profil pro
    Inscrit en
    Février 2007
    Messages
    341
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 341
    Points : 150
    Points
    150
    Par défaut
    OK merci. J'avais trouvé ça sur la doc :

    Les paramètres constante peuvent être transmis à la fonction par valeur ou par référence, selon le compilateur utilisé. Pour obliger le compilateur à transmettre un paramètre constante par référence, vous pouvez utiliser le décorateur [Ref] avec le mot clé const.

    L'exemple suivant illustre comment spécifier le décorateur [Ref] avant ou après le mot clé const :

    function FunctionName(const [Ref] parameter1: Class1Name; [Ref] const parameter2: Class2Name);
    Mais je préfère ton explication ... qui aboutit peut-être à la même chose

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 688
    Points : 13 117
    Points
    13 117
    Par défaut
    Citation Envoyé par navyg Voir le message
    J'avais fait avec le DynArrayUnique pour lequel j'ai eu toutes les peines du monde à trouver la bonne syntaxe.
    DynArrayUnique oblige à créer un type. Une autre façon de procéder est juste de redéfinir la longueur du tableau : SetLength(Dest.a1, Length(Dest.a1)).

    Après un appel à SetLength, il est certain que S référence une chaîne ou un tableau unique, c'est-à-dire une chaîne ou un tableau avec un compteur de références à 1.

  18. #18
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 451
    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 451
    Points : 24 859
    Points
    24 859
    Par défaut
    Clairement, j'aime bien les tableaux dynamiques en retour de fonction avec des types simples pour que le code appelant soit réduit.
    L'appelé alloue explicitement, l'appelant ne se soucie de rien.
    Plus léger que le Create try ... finally Free end pour des types simples.

    Idem pour faire des tableaux de travail local à une fonction, type local aussi, code plus réduit que des TList<> ou autre conteneur.

    Mais on imagine pas à quel point ce compteur de référence des tableaux dynamiques c'est subtil ... c'est pas simple le Delphi, la gestion mémoire reste un sacré sujet, c'est passionnant.
    Finalement, je trouvais le Clonage d'objet parfois pénible, en fait les tableaux dynamiques imbriqués, c'est une vraie science.

    Je suis persuadé d'oublier cela dans dix ans par le fait de ne pas copier des tableaux dynamiques imbriqués vers d'autres.

    Citation Envoyé par navyg Voir le message
    Mais je préfère ton explication ... qui aboutit peut-être à la même chose
    En fait, j'expliqué pourquoi [ref] est nécessaire pour la surcharge de l'opérateur Assign := mais la documentation l'explique de façon général, cela fait très longtemps que j'utilise const pour passer en référence (string, record) mais avec le 64bits, les petits record ont plus de chance d'être copié qu'en 32bits, passer les 8o de la Ref et passer le contenu si <= 8o, en terme de perf, c'est très proche.
    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

  19. #19
    Membre habitué
    Profil pro
    Inscrit en
    Février 2007
    Messages
    341
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 341
    Points : 150
    Points
    150
    Par défaut
    Merci à vous pour toutes ces précisons et astuces
    Avec le class operator ça marche nickel et surtout j'ai appris un truc nouveau !
    Comme disait, parfois dès le matin, un de mes anciens directeur qui était un homme très sage et d'une grande culture :
    "J'ai appris quelque chose de nouveau, je vais me coucher.
    Si tu apprends une chose nouvelle par jour, tu meurs très intelligent".

  20. #20
    Membre habitué
    Profil pro
    Inscrit en
    Février 2007
    Messages
    341
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 341
    Points : 150
    Points
    150
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    DynArrayUnique oblige à créer un type. Une autre façon de procéder est juste de redéfinir la longueur du tableau : SetLength(Dest.a1, Length(Dest.a1)).
    Astuce très intéressante... et surtout une information importante sur la gestion des pointeurs sur les variables en delphi

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 04/09/2013, 13h53
  2. Variables de type Structure
    Par Sphost dans le forum Fortran
    Réponses: 3
    Dernier message: 24/06/2008, 14h51
  3. Réponses: 10
    Dernier message: 17/01/2008, 13h20
  4. Variable de type structure
    Par Houssem dans le forum WinDev
    Réponses: 4
    Dernier message: 08/08/2006, 10h49
  5. Réponses: 6
    Dernier message: 29/04/2006, 20h37

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