OVH Cloud OVH Cloud

Midterm mailing

74 réponses
Avatar
Gabriel Dos Reis
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/#mailing2005-06

-- Gaby

10 réponses

4 5 6 7 8
Avatar
kanze
Marc Boyer wrote:
Marc Boyer wrote:
En ce qui concerne les sockets, je ne suis pas sur
que le besoin soit pattent: POSIX existe, s'interface
bien avec C++.




Je me démande s'il vaut même la peine de réagir à une
bêtise aussi grosse. Je crois qu'il s'agit plutôt d'un
troll.


Heuh, non. D'une naïveté que tu peines à imaginer
peut-être, mais je ne voulais pas faire le Troll.


Alors, la réponse est simple : il n'existe pas de norme pour
l'utilisation des sockets en C++, et la norme pour leur
utilisation en C se limite qu'à un des plateformes qui les
supportent. D'où l'intérête de :

-- un « binding » C++, et

-- qui fasse partie de la norme C++, et non que de une norme
qui ne s'applique qu'à une seule des systèmes d'exploitation
qui les supportent.

Répondre aux question avec « Posix existe » est vraiment
trollasque, puisque franchement, je ne peux pas concevoir
que tu n'es pas au courant qu'il existe des systèmes
non-Posix ;


Si, mais:
- il me semblait (mais je manque d'infos détaillées, ce sont
surtout des infos de deuxième main), quand le support socket
existe, il ressemble énormément à l'interface POSIX. Tout
démenti bienvenu.


Je ne sais pas. J'ai entendu dire que l'interface Windows est
effectivement assez proche. Quant aux interfaces disponibles
dans les processeurs embarqués, je n'en sais strictement rien.

- quand il n'existe pas de support socket, ne devient-il pas
illusoire de vouloir faire une interface socket/C++ ?


J'ai mes doutes en ce qui concerne l'existance d'un système sans
sockets. Même les processeurs embarqués (du genre allumage de
voiture) prévoyent des connections avec le monde extérieur, aux
fins de mise au point ou de la maintenance.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34







Avatar
kanze
Anthony Fleury wrote:
Anthony Fleury wrote:


Bonjour, bon je vais profiter un peu de tout ceci pour poser
des questions idiotes.

Et alors ? parce que c'est disponible dans d'autres langages
on devrait l'ajouter dans C++ ? Je ne sais pas si c'est
l'avis des autres programmeurs C++ ou si la majorité est
contre moi, mais pour ma part je ne veux pas d'un GC dans
C++. Quand j'ai une variable en C++, je sais qu'elle sera
détruite à la fin du bloc si c'est une variable automatique,
à la fin du programme si c'est une static, et quand je vais
faire un delete pour une variable allouée dynamiquement.


Et alors ? Quel rapport avec un glaneur de cellules ? La
présence ou l'absence d'un glaneur de cellules ne change
absolument rien en ce qui concerne les variables
automatiques ou statiques, ni en ce qui concerne le
fonctionnement de delete.


Pour moi, un GC est ce qu'on retrouve en Java. C'est à dire
qu'il rend, pour moi, delete inutile et change un peu le
comportement des variables non automatiques (pour les
automatiques je suis d'accord il ne va pas forcément y changer
quelque chose, même si j'imagine qu'il est possible qu'il
puisse aussi changer le comportement des variables auto, mais
du fait de la compatibilité à garder...)


Java a effectivement un glaneur de cellules. Ce qui ne rend
*pas* inutile le delete quand il fait d'autre chose que
simplement libérer la mémoire. Évidemment, il n'y s'appelle pas
delete, et il n'a pas de syntaxe spécifique au niveau de
langage. Mais ça ne change rien dans le fond.

C'est une chose très importante dont il faut se rendre compte :
un glaneur de cellules ne fait rien en ce qui concerne la durée
de vie des objets. Il glane les cellules de la mémoire, c'est
tout. S'il faut que ton objet fasse quelque chose de concret à
la fin de sa vie, il faut bien appeler la fonction qui le fait.
Que ce soit du C++ ou du Java. En C++, cette fonction s'appelle
un destructeur, et il est appelé automatiquement quand la
variable sort de portée, pour les objets automatiques ou
statiques. (Évidemment, Java n'a pas d'objets automatique ni
statiques.) Pour les objets dynamiques, le C++ a une syntaxe
particulière, delete, qui s'occupe *aussi* de la gestion de la
mémoire. Du fait qu'elle sert à la fois à la durée de vie et à
la gestion de la mémoire, il s'avère qu'il faut l'appeler même
dans les cas (assez fréquents dans mon code, au moins) où
l'objet n'a rien de particulier à faire en fin de vie. C'est du
travail (et une source d'erreurs) en plus.

Pour l'instant, je ne crois pas qu'il y a un consensus en ce
qui concerne la façon exacte que le GC s'integrera dans le
C++, mais ce qui est certain, c'est qu'il ne changera pas la
sémantique des programmes existants. À moins qu'ils ont des
fuites de mémoire, évidemment.


Pour moi, c'est la seule application du GC que je vois, éviter
les fuites mémoires, et par ailleurs, supprimer l'utilisation
de delete en gros.


Et alors... Dans mon code, il doit y avoir plus de 90% des
classes dont le destructeur ne fait que s'occuper de la mémoire.
Actuellement, il faut quand même que j'écrive ces destructeurs.
Et que je m'assure qu'ils soient appelés tôt ou tard -- mais pas
trop tôt. C'est du travail dont je pourrais bien me passer.

Mais je ne suis pas sûr que mes connaissances soient assez
bonnes dans ce domaine. Auriez vous un bon lien qui me donne
une description de ce qu'est un GC. La seule chose que je
trouve avec Google ce sont des références qui me donne les
descriptions de ce que serait un GC dans C ou C++, et de ce
qu'est le GC en java.


Le meilleur point de départ, c'est peut-être la FAQ de GC :
http://www.iecc.com/gclist/GC-faq.html.

Pourquoi avoir un GC ??
Pour réduire la quantité de travail nécessaire pour écrire un

programme correct.


Ca je comprend. Mais son rôle serait il seulement de remplacer
des outils genre valgrind et donc réduire le temps de
développement ?


Pas du tout. Il y aurait bien moins de destructeurs à écrire,
par exemple. Et une fois que l'analyse determine qu'une classe
n'a rien à faire à la fin de sa vie, on peut même faire économie
d'une certaine partie de l'analyse de ses utilisations.

(enfin perso si il est inclut ca me plairait de récupérer le
comportement normal de mon compilateur C++)


La compatibilité avec l'existant me semble essentielle. La
possibilité de ne pas utiliser le GC aussi ; il existe des
applications où il ne convient pas (même si elles sont moins
nombreuses qu'on pourrait le croire).


Et de toute facon la proposition parle bien de pragma pour en
enlever les effets, donc il est certain que la compatibilité
serait gardée.


Ça, ce n'est pas sûr qu'il passe. Certains n'aiment pas les
pragma, d'après ce que j'ai entendu dire.

et requiert-il vraiment que des concepts comme les sockets
soient inclus dans la bibliothèque standard d'un langage qui
se veut plus général que ca ?


Oui et non. Disons que l'argument est à peu près le même que
pour des entrées/sorties sur fichier. On pourrait bien
imaginer un langage qui ne supporte ni l'un ni l'autre.


Ma première réponse est un peu idiote en fait je pense, car je
me suis laissé emporter par mon "expérience" je crois.

Je connais surtout C et C++, et les seules fois où j'ai
utilisé les sockets, je l'ai fait sans problème avec POSIX,
cependant je n'avais pas autant de contraintes de portabilité
que vous pouvez avoir. Ca se restreignait aux *nix. J'ai aussi
fait du Java qui m'a montré un langage avec un peu plus de
supports.

Cependant, il en reste que pour moi, C et C++ sont des
langages qui m'ont habitué à ne pas avoir ces supports
(sockets et GUI), et je me suis toujours arrangé dans mes cas
(assez loin de vos "énormes" projets) avec POSIX, wxWidget &
Co.


Mais ça fait que dans chaque application, tu as créé ta propre
classe de socket, n'est-ce pas ? Et qu'en fin de compte, il y
avait trois où quatre classes différentes pour faire la même
chose dans l'application -- puisque tes bibliothèques tièrces ne
se servaient pas de ta classe (et tu n'avais pas accès aux
leurs).

Mais à partir de là, je me pose une question. Comment se
décide dans un comité l'introduction d'un nouveau support ? je
suppose que déjà ca ne doit pas casser l'existant. Ensuite, ca
doit être utile. Cependant cette notion est relative. En
effet, il pourrait y avoir de nombreuses choses dans C++
d'ajouté : Sockets et GUI dont on parlait, mais aussi
Multithreading comme ce qu'a cité Stan dans son premier post,
avec on peut l'imaginer un support des Mutex, une meilleure
réentrance dans la bibliothèque standard (cependant les
problèmes n'arrivent de tête que sur les parties communes avec
C, ce qui casserait la compatibilité avec C), support des
IPCs...


C'est effectivement complexe. Le premier critère, avant
l'utilité, etc., c'est qu'il faut qu'il y ait quelqu'un qui est
prêt à faire le travail. Ensuite, tout dépend. Un critère
important (non forcément essentiel, mais qui fait qu'il faut
normaliser), c'est qu'il soit difficile, sinon impossible, à
faire dans la contexte du langage standard -- c'est le cas, par
exemple, des entrées/sorties (ou des sockets), voire qu'il exige
du support au niveau langage (le GC, les threads, lambda...). Un
autre critère, peut-être aussi important, c'est qu'il s'agit de
quelque chose qui risque de servir partout, à la fois dans les
bibliothèques des tièrces que dans l'application -- c'est le cas
de string, vector et complex. Ces deux critères sont des
arguments très forts pour la normalisation.

Au delà, c'est un peu en fonction des propositions -- si
quelqu'un veut investir l'effort de faire une proposition
argumentée, et que la chose semble raisonablement utile à une
partie importante des programmeurs, il y a des chances que son
proposition soit prise en considération.

Alors comment décider ce qui peut être inclut dans C++ ? ce
qui doit y entrer ? Comment choisir entre support d'une
fonction, et ne pas rentre le langage trop "complexe" et trop
"gros" avec un support de trop de choses qui pourraient être
faites autrement ? Comment tracer cette limite en fait entre
ce qui doit être dans le langage et ce qui ne doit pas y être
?

Pour ma part, je suis trop habitué à n'avoir ni socket, ni GUI
en C et en C++. Je n'ai sûrement pas assez conscience des
vrais problèmes d'un gros gros projet comme ceux que vous
développez. D'où mes questions qui peuvent paraitre idiotes.


Pas du tout.

En ce qui concerne les sockets, il y a plusieurs arguments
pour : le plus fondamental, c'est qu'on ne peut pas les
implémenter dans le contexte du langage, sans recourir à des
extentions propre à la plate-forme (et du point de vue de C/C++,
Posix est une extension propre à la plate-forme). Et aussi le
fait qu'ils risquent de servir dans plusieurs bibliothèques à la
fois, voire même apparaître à l'interface de la bibliothèque.

En ce qui concerne le GUI, le problème est plus complexe. C'est
certain qu'on ne peut pas l'implémenter sans recours à ce qui
est propre à la plate-forme. En revanche, c'est peu probable
qu'on ait plusieurs bibliothèques qui s'en servent, chacune avec
leur propre version, et ce que supportent les differentes
plate-formes est loins d'être semblables -- ce n'est pas évident
de trouver un compromis qui offre toute la fonctionalité voulue
*et* qui se laisse implémenter de façon efficace partout. En
revanche, si on avait une GUI normée, on pourrait bien imaginer
des bibliothèques tièrces qui s'y basent, mais qui offrent des
fonctionnalités en plus. C'est ce qui s'est passé en Java, par
exmple. Je dirais donc qu'avoir une GUI normalisée n'est pas
sans intérêt, mais c'est loin d'être aussi essentiel que les
sockets, et c'est nettement plus de travail. Pour l'instant, je
ne suis pas vraiment à jour, mais j'ai l'impression qu'il n'y a
pas de volentiers pour ce travail, ou au moins pas assez -- ce
qui fait qu'on n'aurait sûrement pas de GUI standard. (Mais Gaby
sait peut-être des choses que je ne sais pas là-dessus.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
Fabien LE LEZ
On 11 Jul 2005 01:02:15 -0700, :

[delete]
Du fait qu'elle sert à la fois à la durée de vie et à
la gestion de la mémoire, il s'avère qu'il faut l'appeler même
dans les cas (assez fréquents dans mon code, au moins) où
l'objet n'a rien de particulier à faire en fin de vie. C'est du
travail (et une source d'erreurs) en plus.


Il y a un point qui me gêne dans ce que tu dis : si l'objet n'est pas
un type de base, tu ne peux pas savoir si le destructeur est vide ou
pas (c'est un détail d'implémentation, qui peut changer en cours de
route). Donc, tu dois l'appeler systématiquement, "au cas où".

Et franchement, si je suis responsable de la création de la classe
_et_ du code qui l'utilise, je préférerais encore m'encombrer de
quelques delete que de m'engager à ce que le destructeur reste vide.

Avatar
adebaene
On Fri, 08 Jul 2005 21:20:43 +0200, Anthony Fleury
:

C'est à dire qu'il rend, pour moi, delete inutile


Même pas, il me semble : delete (directement ou via les pointeurs
intelligents) sert toujours si on veut contrôler la durée de vie
d'objets alloués par new.


Il me semble qu'il y a une confusion dans la discussion sur le sujet :
Ce qu'on appelle courramment "destruction" d'un objet se décompose en
2 phases :
- appel du destructeur. En C++, c'est appel doit être réalisé
lorsqu'on sort de la portée de déclaration de la varaible, pour reser
compatible avec l'existant.
- libération de la mémoire ("delete") si l'objet a été allloué
dynamiquement.

Ces 2 opérations sont indépendantes et peuvent être traitées
différemment. La nouvelle mouture du "C++ .NET" de Microsoft,
C++/CLI, prend justement cette option :
- le destructeur d'un objet "managé" est appelé lorsqu'il sort du
scope (comme en C++ classique).
- la mémoire correspondante est gérée par le GC et est libérée
"plus tard". Dans ce cas là, comme le destructeur de l'objet a déjà
été alloué, le GC n'a pas à se soucier d'appeler un "finaliseur" à
la Java, ou tout autre concept du même ordre.

Au fait, comment le GC sait-il qu'un objet peut être détruit ?
Ca, c'est le problème de l'implémentation, mais généralement ca

entraine certaines contraintes, par exemple l'interdiction de faire de
l'arithmetique de pointeur (ceci-dit, je ne suis pas sûr que ce soit
un mal d'interdire cela! ;-)

Arnaud


Avatar
Fabien LE LEZ
On 11 Jul 2005 05:07:32 -0700, :

Il me semble qu'il y a une confusion dans la discussion sur le sujet :
Ce qu'on appelle courramment "destruction" d'un objet se décompose en
2 phases :
- appel du destructeur. En C++, c'est appel doit être réalisé
lorsqu'on sort de la portée de déclaration de la varaible, pour reser
compatible avec l'existant.


Quelle portée ?

Si on parle d'objets automatiques, il n'y a aucun problème : l'objet
est effectivement détruit lors de la sortie du bloc, le destructeur
est appelé, on sait que la mémoire sera libérée (l'OS est -- il me
semble -- libre de la libérer immédiatement ou pas), tout va bien.
Même pas besoin de GC (ou du moins, s'il y en a un, fourni par le
compilateur ou par l'OS, le programmeur n'en a cure).

Pour les pointeurs intelligents, c'est à peu près pareil (ce n'est
jamais que de l'allocation dynamique avec les avantages des objets
automatiques).

L'utilité du GC, et les problèmes, commencent quand on parle d'objets
alloués dynamiquement, sans utilisation de pointeurs intelligents.

Si j'écris "Machin* ptr= new Machin;", pour l'instant, le compilateur
ne sait pas quand il va devoir appeler le destructeur ou libérer la
mémoire. Et justement, si j'utilise l'allocation dynamique, c'est
parce que je ne veux pas que l'objet soit détruit en fin de bloc ; au
contraire, je veux pouvoir choisir exactement le moment de l'appel au
destructeur.
D'ailleurs, certains objets ne sont même pas prévus pour être détruits
(les singletons par exemple).


- le destructeur d'un objet "managé" est appelé lorsqu'il sort du
scope (comme en C++ classique).


Comme un objet automatique du C++ classique, c'est bien ça ?

Avatar
kanze
wrote:

[...]

Au fait, comment le GC sait-il qu'un objet peut être détruit ?


Ca, c'est le problème de l'implémentation, mais généralement
ca entraine certaines contraintes, par exemple l'interdiction
de faire de l'arithmetique de pointeur (ceci-dit, je ne suis
pas sûr que ce soit un mal d'interdire cela! ;-)


D'où est-ce que tu as ça ? Parce que les collecteurs que je
connais permettent tous l'arithmétique sur des pointeurs. (On
aurait du mal à l'intérdire, étant donné la définition de
l'opérateur [].)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Anthony Fleury
Merci beaucoup pour ces réponses toujours aussi détaillées !


Pour les objets dynamiques, le C++ a une syntaxe
particulière, delete, qui s'occupe *aussi* de la gestion de la
mémoire. Du fait qu'elle sert à la fois à la durée de vie et à
la gestion de la mémoire, il s'avère qu'il faut l'appeler même
dans les cas (assez fréquents dans mon code, au moins) où
l'objet n'a rien de particulier à faire en fin de vie. C'est du
travail (et une source d'erreurs) en plus.


Oui, mais pour moi le GC justement se serait chargé, lorsque l'objet
serait devenu "inutile", de récupérer la mémoire, mais après avoir
appelé le destructeur. En fait, il jouait le rôle du delete que l'on
appelle au moment où l'on se rend compte que notre variable devient
inutile.

Et alors... Dans mon code, il doit y avoir plus de 90% des
classes dont le destructeur ne fait que s'occuper de la mémoire.
Actuellement, il faut quand même que j'écrive ces destructeurs.
Et que je m'assure qu'ils soient appelés tôt ou tard -- mais pas
trop tôt. C'est du travail dont je pourrais bien me passer.


En effet...

Le meilleur point de départ, c'est peut-être la FAQ de GC :
http://www.iecc.com/gclist/GC-faq.html.


Merci je vais lire ca avec intérêt

Pas du tout. Il y aurait bien moins de destructeurs à écrire,
par exemple. Et une fois que l'analyse determine qu'une classe
n'a rien à faire à la fin de sa vie, on peut même faire économie
d'une certaine partie de l'analyse de ses utilisations.


En fait, tous les destructeurs triviaux et les destructeurs se chargeant
juste de faire des libérations d'objets alloués à l'intérieur de la
classe en gros deviendraient obselètes, et permettraient donc un moins
grand nombre de fuites mémoire. J'ai bien compris ? et pour toutes ces
types, on ferait l'économie du delete. Bref je vais me plonger dans la
FAQ pour comprendre toutes ces subtilitées.

Mais ça fait que dans chaque application, tu as créé ta propre
classe de socket, n'est-ce pas ? Et qu'en fin de compte, il y
avait trois où quatre classes différentes pour faire la même
chose dans l'application -- puisque tes bibliothèques tièrces ne
se servaient pas de ta classe (et tu n'avais pas accès aux
leurs).


En effet... Ces arguments et la suite me donnent une bien meilleure
vision de ce qui peut vraiment compter dans l'inclusion d'une
fonctionnalité dans le standard d'un langage. Merci pour ces
explications et à l'avenir je tenterai de modérer mes propos et de mieux
m'expliquer aussi :-)

--
Anthony Fleury

Avatar
Arnaud Debaene
Fabien LE LEZ wrote:
On 11 Jul 2005 05:07:32 -0700, :

Il me semble qu'il y a une confusion dans la discussion sur le sujet
: Ce qu'on appelle courramment "destruction" d'un objet se décompose
en 2 phases :
- appel du destructeur. En C++, c'est appel doit être réalisé
lorsqu'on sort de la portée de déclaration de la varaible, pour reser
compatible avec l'existant.


Quelle portée ?

Si on parle d'objets automatiques, il n'y a aucun problème : l'objet
est effectivement détruit lors de la sortie du bloc, le destructeur
est appelé, on sait que la mémoire sera libérée (l'OS est -- il me
semble -- libre de la libérer immédiatement ou pas), tout va bien.
Même pas besoin de GC (ou du moins, s'il y en a un, fourni par le
compilateur ou par l'OS, le programmeur n'en a cure).

Pour les pointeurs intelligents, c'est à peu près pareil (ce n'est
jamais que de l'allocation dynamique avec les avantages des objets
automatiques).
Je me suis mal exprimé. L'appel automatique au destructeur en C++/CLI ne

s'effectue effectivement que sur les variables ayant une portée locale
(alloués sur la pile)

L'utilité du GC, et les problèmes, commencent quand on parle d'objets
alloués dynamiquement, sans utilisation de pointeurs intelligents.
Dans ce cas, tous les GC que je connais fournissent un "finaliseur", ou une

notion qui s'en raproche.

Si j'écris "Machin* ptr= new Machin;", pour l'instant, le compilateur
ne sait pas quand il va devoir appeler le destructeur ou libérer la
mémoire.
Ave un GC, se sera le finaliseur qui sera appelé, pas le destructeur.



- le destructeur d'un objet "managé" est appelé lorsqu'il sort du
scope (comme en C++ classique).


Comme un objet automatique du C++ classique, c'est bien ça ?
Oui : ca permet d'avoir l'idiome RAII avec un GC.


Arnaud


Avatar
Arnaud Debaene
wrote:
wrote:

[...]

Au fait, comment le GC sait-il qu'un objet peut être détruit ?


Ca, c'est le problème de l'implémentation, mais généralement
ca entraine certaines contraintes, par exemple l'interdiction
de faire de l'arithmetique de pointeur (ceci-dit, je ne suis
pas sûr que ce soit un mal d'interdire cela! ;-)


D'où est-ce que tu as ça ? Parce que les collecteurs que je
connais permettent tous l'arithmétique sur des pointeurs. (On
aurait du mal à l'intérdire, étant donné la définition de
l'opérateur [].)
Pas toute l'arithmetique de pointeur : typiquement, les casts depuis/vers

void* sont interdits il me semble.

Ensuite pour utiliser l'opérateur [], si le GC fournit un compactage du tas
(et s'il peut donc déplacer les objets en mémoire), il faut généralement
"pinner" l'objet que l'on souhaite déréférencer pendant que l'on effectue
ses opérations.

Arnaud



Avatar
kanze
Arnaud Debaene wrote:
Fabien LE LEZ wrote:


[...]
L'utilité du GC, et les problèmes, commencent quand on parle
d'objets alloués dynamiquement, sans utilisation de
pointeurs intelligents.


Dans ce cas, tous les GC que je connais fournissent un
"finaliseur", ou une notion qui s'en raproche.


Mais un finaliseur n'est pas du tout la même chose qu'un
destructeur. Il est appelé (s'il est appelé) dans une toute
autre contexte.

Si j'écris "Machin* ptr= new Machin;", pour l'instant, le
compilateur ne sait pas quand il va devoir appeler le
destructeur ou libérer la mémoire.
Ave un GC, se sera le finaliseur qui sera appelé, pas le

destructeur.


À condition qu'il a un finaliseur. Où qu'il en a inscrit un --
dans certaines implémentations, la finalisation n'est pas
automatique.

Aussi, la finalisation n'a pas toujours lieu.

Ne confond pas la finalisation avec la destruction. Si l'objet a
besoin de destruction (au sens actuel) pour d'autres raisons que
la gestion de la mémoire, il faudra toujours appeler delete,
même avec un glaneur de cellules.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


4 5 6 7 8