Ce n'est pas mon genre de crier au bug dans un langage de programmation et même j'ai plutôt tendance à me méfier des personnes qui font habituellement ce genre de déclaration (car dans 99% des cas c'est plutôt de la mauvaise programmation), mais dans mon cas et après avoir passé une après-midi à chercher un bug dans un code (qui il faut le dire est assez complexe) j'en ai conclu qu'il y a un problème dans la gestion des 'string' dans Delphi.
Pour m'en assurer j'ai fait le test suivant (nouveau projet vierge, un simple bouton 'Button1' sur la fiche, ajouter un gestionnaire sur le click du bouton puis copier/coller le code ci-dessous) :
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 unit Unit1; interface uses Forms, Classes, Controls, StdCtrls, Dialogs; type TTestEvent = procedure ( const Value: string ) of object; TTest = class public procedure Show( const AText: string; SetParentText: TTestEvent ); end; TForm1 = class(TForm) Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); private FText: string; FTest: TTest; public procedure TestShow; procedure SetText( const Value: string ); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin FTest:= TTest.Create; end; procedure TForm1.FormDestroy(Sender: TObject); begin FTest.Free; end; procedure TForm1.TestShow; begin SetText( 'Message1' ); FTest.Show( FText, SetText ); end; { TTest } procedure TTest.Show( const AText: string; SetParentText: TTestEvent); begin ShowMessage( AText ); SetParentText( 'Message2' ); ShowMessage( AText ); end; procedure TForm1.Button1Click(Sender: TObject); begin TestShow; end; procedure TForm1.SetText(const Value: string); begin FText:= Value; end; end.
En fait on pourrait simplifier le problème en disant que cela vient du fait que le compilateur envoie un pointeur du string lorsqu'on précise 'const', seulement voilà d'une part le développeur n'est pas censé le savoir, d'autre part avec les compteurs de référence Delphi devrait savoir qu'il ne faut pas écraser le contenu de l'ancienne chaîne de caractères lors de la seconde affectation car il reste une référence dessus. Hors là il "libère" la mémoire ce qui fait que si vous exécutez ce test vous ne verrez pas "Message1" puis "Message1" (et non "Message2") comme cela devrait être le cas mais "Message1" et puis le contenu de la même zone mémoire invalidée (ce qui dans mon cas sous Delphi 7 correspondant à "Project1", le nom de mon projet), ou mieux une violation d'accès ...
Alors BUG ou pas BUG ?? pour l'instant et jusqu'à ce qu'on me prouve le contraire c'est un BUG. Un argument de type 'const' ne peut-être modifié dans l'ensemble d'un "scope" et là ce n'est pas le cas.
Ce qui m'étonne encore plus c'est que Delphi alloue une nouvelle zone mémoire au pointeur alors que la nouvelle chaîne à la même taille (je m'attendais d'abord à avoir "Message2"...), donc cela laisse à penser qu'il a détecté que l'ancien zone mémoire est toujours référencé, mais le contenu de l'ancienne est visiblement "invalidé" car il l'utilise de suite pour y stocker d'autres infos, enfin bref... je suis vert, moi qui préconisait à tout le monde dans ma boite de toujours utiliser les 'const' pour les 'string', 'record' et 'array' utilisés comme argument, ça remet tout en cause !!!
Pour le moment j'ai corrigé un problème d'erreur "violation d'accès" ou "erreur interne" aléatoire juste en supprimant 'const' dans mon prototype de fonction et je trouve cela absoluement anormal !
Peut-être quelqu'un pourra-t-il me rassurer en m'indiquant que j'ai fait quelque chose de mal, qu'il y a un correctif, ou une autre façon de faire (sans se passer du 'const' qu'il doit y avoir en 10000000 d'exemplaire dans mon projet depuis le temps que j'ai passé la consigne) ?
Merci de votre aide, si aide possible il y a (sinon merci de votre compassion) car au vue de ce problème je pourrai simplement passer mon string sans préciser 'const' devant bien sûr, mais dans ce cas comment savoir si je ne vais pas avoir le bug ailleurs ? Autant supprimer tous les 'const', et ralentir atrocement certaines fonctions récursives qui avaient gagné beaucoup avec ce fameux 'const' ??? Sans compter que j'en dormirai plus...
[Edit] en passant 'var' plutôt que 'const' je n'ai plus de messages d'erreur mais à la place mes chaînes de caractères constantes voir leur contenu modifié dans le même scope, c'est toujours pas logique mais au moins le Delphi va réallouer le pointeur plutôt que d'en allouer un nouveau (et que l'ancien pointe sur une zone mémoire plus allouée)... Hum je me demande bien ce que le Delphi fout en interne avec sa gestion de chaînes de caractères, quelqu'un pourrait-il m'expliquer ?
Partager