Le C++/CLI n'est pas pour moi l'introduction d'un GC en C++ : Ce serait plutôt l'introduction du C++ dans un système à GC.
Version imprimable
Le C++/CLI n'est pas pour moi l'introduction d'un GC en C++ : Ce serait plutôt l'introduction du C++ dans un système à GC.
Bon apparemment mon lien est toujours pas bon (désolé j'ai du mal avec ce service de google - peut-être que tout simplement le lien devient invalide quand le fil est modifié ?).
Je faisais référence à mon message sur le RAII tout ça.
Le ramasse-miettes, C++/CLI etc. c'est hors sujet ici.
On a juste un type de pointeur particulier, ainsi qu'un gcnew. (enfin en C++/CLI il faut quand même que la classe soit déclarée comme étant utilisable avec le ramasse-miettes qui indique si on doit autoriser son usage avec le GC ou pas)Citation:
Lier l'utilisation ou pas du GC au système de type, bien qu'attrayant au premier abord, ne marche pas vraiment, puisque le choix d'avoir une donnée partagée n'est pas du ressort de la donnée elle même, mais de l'usage que l'on soihaite en faire.
Ce type de pointeur est similaire à un pointeur intelligent comme shared_ptr par exemple, mais en mettant ça dans le langage, on peut rendre ça plus performant.
Le M-Id. Il n'y a que ça de vrai.Citation:
Envoyé par loufoque
L'absence de réponse, ce n'est pas parce que tu avais répondu à un trolleur non initié?
Autrement, il me semble que James cite régulièrement l'exemple des programmes multithreadés comme application pratique des GC en C++. Ceci dit, le framework que j'utilise au taf' (ACE) me pousse vers les queues de messages. Du coup, je suis, pour l'instant, passé à côté des GC appliqués au MT.
Je pense avoir lu le bon message.Citation:
Envoyé par loufoque
Ah bon ? Quand il s'agit du discussion sur les bonnes pratiques du C++ et de son évolution, il me semble légitime de citer les langages qui lui sont proches.Citation:
Envoyé par loufoque
Plus que simplement utilisable par le GC, on la déclare comme utilisable uniquement par le GC. C'est ça que je trouve étrange.Citation:
Envoyé par loufoque
Introduire un pointeur spécifique pose le problème de savoir ce que l'on fait en cas d'interaction avec d'autres pointeurs.Citation:
Envoyé par loufoque
Supposons qu'on note # ce pointeur, et # l'opérateur de déréférencement. Que faire si l'on a :
OuCode:
1
2 int #i = gcnew int(42); int *j = & (#i);
Code:
1
2
3 int *i = new int(42); int #j = & (*i); delete i;
C'est simple, il suffit de ne pas permettre d'assigner un T* à une variable de type T#.
Le premier exemple ne pose pas de problème particulier, si ce n'est que le GC ne prend pas en compte j, ce qui signifie accessoirement que i peut être libéré même si j pointe vers la valeur.
A priori partager les données plutôt que de les transmettre est plus performant sur les systèmes SMP, qui sont tout de même plus populaires.Citation:
Autrement, il me semble que James cite régulièrement l'exemple des programmes multithreadés comme application pratique des GC en C++. Ceci dit, le framework que j'utilise au taf' (ACE) me pousse vers les queues de messages.
J'ai bien respecté cette règle dans mon code. Je déréférence un pointeur d'un type, et je met l'adresse du résultat dans le pointeur du second type. Je ne fais pas d'affectation directe. A partir du moment où on peut déréférencer les pointeurs, et prendre l'adresse de l'objet pointé, j'ai du mal à voir comment empêcher qu'un pointeur géré par GC et un autre non pointent sur le même objet. Il y a d'ailleurs le même problème avec les smart_ptrs boost.Citation:
Envoyé par loufoque
Si t est de type T, alors &t est de type T* const.Citation:
Je déréférence un pointeur d'un type, et je met l'adresse du résultat dans le pointeur du second type.
i étant de type int*, *i est de type int, et &(*i) est de type int* const.
Dans ce genre de situation, je vois 3 alternatives pour lopérateur d'adressage :
- Il ne retourne qu'un pointeur non géré par le GC, et rien d'autre (j'ai un peu l'impression que c'est ce que tu proposes. Peut-être que ça a du sens, faut que je creuse),
- Il retourne soit un pointeur géré par GC, soit un pointeur non géré en fonction de son contexte d'utilisation,
- Il y a deux opérateurs d'adressages. L'un retournant un pointeur géré, lautre non.
J'avais écrit mon code dans l'hypothèse 2. Je me rend compte qu'il aurait été plus clair dans l'hypothèse 3, en notant par exemple % l'opérateur qui retourne une adresse gérée par le GC.
Code:
1
2
3
4
5
6 int *i = new int(42); int #j = %(*i); delete i; int #i = gcnew int(42); int *j = & (#i);
Ton opérateur % n'a pas de raison d'être. Bien que demander l'adresse de n'importe quel objet ait du sens (puisque tout objet a une adresse d'après le modèle de C++, sauf optimisations avec les registres), demander à partir de n'importe quel objet à obtenir une référence intelligente vers un élément stocké dans un endroit spécial et qui peut être automatiquement libéré quand il n'est plus référencé est tout simplement impossible.
Ce serait comme demander à savoir dans quel fichier sur le disque dur est stockée une variable en mémoire vive.
Ton hypothèse 2 n'a pas de sens ; l'adresse d'une variable c'est l'adresse d'une variable, juste un genre d'entier qui donne un emplacement en mémoire. Ça n'a conceptuellement rien à voir avec une référence vers un objet géré par le ramasse-miettes, bien que le second contienne forcément le premier d'un point de vue implémentation.
J'utilise le terme de référence intelligente plutôt que de pointeur parce que cela est plus adapté dans le cadre de l'utilisation d'un GC.
Il ne s'agit plus vraiment d'une adresse qu'on peut éventuellement déréférencer mais d'une référence vers un objet forcément valide, ou d'une référence nulle.
Ce n'est néanmoins pas une référence au sens C++ du terme, puisqu'on peut changer l'objet référencé.
De toutes manière ajouter cela au langage n'est qu'une optimisation de ce qu'on pourrait faire avec une bibliothèque.
Cela aurait bien sûr une interface totalement différent de shared_ptr, qui ne fait que gérer des pointeurs alloués avec new qu'on lui fournit (et qu'on peut d'ailleurs lui reprendre).
Si j'en avais la compétence j'implémenterais bien ce genre de chose.
Surtout que c'est une erreur de se dire "tant que je garde une référence active vers un objet, tous les pointeurs vers celui-ci resteront valides : Le ramasse-miettes ppeut à tout moment déplacer une variable en mémoire afin de faire de la place...
Le seul antidote à cela sont par exemple les pin_ptr<> du C++/CLI...
C'est pas vraiment possible avec la proposition de Boehm.Citation:
Le ramasse-miettes ppeut à tout moment déplacer une variable en mémoire afin de faire de la place...
Effectivement, le GC proposé par Boem ne permet pas de servir de défragmenteur de mémoire. Par contre, il évite d'avoir une double indirection pour accèder aux données pointées. Je ne sais pas ce qui est le plus efficace (sachant qu'on est dans un contexte où comme il y aura aussi des pointeurs non gérés pas GC, il y aura toujours des problèmes de fragmentation).
Il y a des moments où bien que tout ce qu'on ait à notre disposition, ce soit un objet, on sait par ailleurs que cet objet est géré par un GC, et on aimerait bien retourner une référence vers lui (d'ailleur, cet opérateur existe en C++/CLI, et s'écrit %). Le cas le plus classique auquel je pense, c'est depuis une fonction membre de l'objet (le shared_from_this() de boost, du moins dans les cas où il marche...).Citation:
Envoyé par loufoque
Autrement ce que tu proposes a l'air assez proche de ce qu'il y a en C++/CLI, à l'exception près qu'un tel poiteur serait utilisable pour tous les types, et non uniquement les types spécifiquement faits pour. Intuitivement, j'aurais tendance à aller dans cette direction explicite, même si je pense que telle que décrite elle laisse des zones floues actuellement.
Un autre point d'intéraction entre les types de poniteurs auquel je pense (même notation):
Dans ce cas, peut-on collecter ? Après tout, a est connu par un pointeur non managé, donc peut donc être considéré comme non collectable. Ou alors, on considère que l'on doit aussi remonter à travers les pointeurs non managés pour déterminer des objets non atteignables ?Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 struct A { B* b; }; struct B { A #a; }; { A #a = gcnew A; a->b = new B; a->b->a = a; }
L'approche proposée par Boem est différente, en celà qu'elle me semble plus facilement compatible avec du code existant.
Je suis loin d'en être certain. Après tout, Boem propose une solution bibliothèque depuis longtemps, s'il veut une solution langage, ce n'est probablement pas sans raison. Et le fait qu'il ait pas mal travaillé sur le modèle mémoire en multithread n'y est probablement pas étranger. Mais j'avoue ne pas avoir lu dans les détails ces documents.Citation:
Envoyé par loufoque
Sans même parler d'implémentation, je serais curieux de voir une spec détaillée de la chose, afin de mieux juger de comment elle peut tenir la route.Citation:
Envoyé par loufoque
J'avais cru lire un jour qu'un GC correct ne pouvait pas être implémenté en C++ à l'aide d'une librairie mais qu'il devrait l'être au niveau du langage même.
(j'essayerai de voir si je retrouve ce truc qui doit dater sûrement, alors ça a peut être changer)
Il y a quelques effets qui font qu'un GC en C++ tel qu'il est actuellement est formellement impossible: on peut cacher des pointeurs (dans un fichier, par un algo d'encryptage ou de compression; ce n'est uniquement theorique: il y a un moyen d'implementer une forme restreinte de listes doublement chainee avec un seul pointeur et je l'ai vu utiliser en pratique) et donc un GC ne peut pas determiner formellement dans tous les cas si un pointeur est accessible ou non. Les optimisations peuvent aussi cacher parfois des pointeurs. En pratique rare sont les programmes qui utilisent la possibilit de cacher les pointeurs et les optimisations ne cachent pas les pointeurs mais si on veut une garantie (le probleme est que sans garantie, le GC peut liberer de la memoire qui ne le devrait pas) il faut traiter ces cas limites et ca ne peut se faire que dans la description du langage.Citation:
Envoyé par Hylvenir