je suis en train de développer une bibliothèque C++, et je rencontre des
problèmes à l'exécution avec certains couples machines-compilateurs
(plantages de toute la batterie de tests unitaires sur différents objets
STL) qui me font douter de certains usages C++ concernant la définition
de chaînes constantes (même si pourtant cela a l'air simple, ou devrait
l'être !).
Dans un fichier CeylanUniformResourceIdentifier.h je déclare :
J'ai un doute car ProtocolSeparator est un objet qui serait à construire
avant la première instruction de main (licite ? dangereux ?), et
valgrind me sort :
"""
==30157== Invalid write of size 4
==30157== at 0x40D3165: _GLOBAL__I__ZN6Ceylan3URI17ProtocolSeparatorE
(CeylanTypes.h:432)
==30157== by 0x40D3324: (within
/home/sye/tmp-Ceylan-test-install/lib/libCeylan-0.3.so.0.0.3)
==30157== by 0x40524C8: (within
/home/sye/tmp-Ceylan-test-install/lib/libCeylan-0.3.so.0.0.3)
==30157== by 0x400B70C: call_init (in /lib/ld-2.3.6.so)
==30157== by 0x400B7F1: _dl_init (in /lib/ld-2.3.6.so)
==30157== by 0x400081E: (within /lib/ld-2.3.6.so)
==30157== Address 0xBEFFCBD8 is not stack'd, malloc'd or (recently) free'd
"""
_GLOBAL__I__ZN6Ceylan3URI17ProtocolSeparatorE décrypté donne : "global
constructors keyed to Ceylan::URI::ProtocolSeparator". Mon doute est
accentué par le fait que la référence mentionnée (CeylanTypes.h:432) n'a
strictement aucun rapport avec ProtocolSeparator et pointe sur :
"""
typedef std::list<Ceylan::Uint16>::size_type ListSize ;
"""
(c'est un raccourci d'écriture [et peut-être aussi une mauvaise
pratique] pour fournir au développeur un type plus simple à manier).
Je sais qu'il serait possible d'encapsuler chacune de ces chaînes
constantes par une fonction spécifique qui la retourne (ex :
) mais je me passerais bien d'une telle encapsulation si elle est
facultative.
Vous serait-il possible de m'indiquer si d'emblée, purement sur le plan
du langage C++, ces formes sont correctes ? (après je sens que je vais
être bon pour continuer sur les listes de gcc et de valgrind)
En effet, même si c'est peut-être moins "C++" comme approche.
Finalement mon code en lui-même était correct, mais le passage aux autotools m'avait fait en profiter pour rajouter -fstack-check dans AM_CXXFLAGS, ce qui conduit manifestement au moins certaines versions de g++ à générer du code absolument calamiteux.
Encore merci à tous,
Olivier.
Olivier Boudeville wrote:
Merci à tous pour vos réponses,
je déduis de tout cela que les trois formes :
- 'const std::string s = "foo";' dans le header - 'extern const std::string s ;' dans le header + "const std::string s = "foo" ;' dans l'implémentation - 'const std::string & get_s() ;' + sa définition
La quatrième forme 'extern char const s[];' dans le header + 'char const s[] = "foo";' dans l'implémentation ne présente pas le problème d'ordre d'initialisation des objets globaux qui se trouvent dans des unités de compilation différentes (cf. § 3.6.2/1) et nécessite pas l'introduction d'une fonction.
Falk
En effet, même si c'est peut-être moins "C++" comme approche.
Finalement mon code en lui-même était correct, mais le passage aux
autotools m'avait fait en profiter pour rajouter -fstack-check dans
AM_CXXFLAGS, ce qui conduit manifestement au moins certaines versions de
g++ à générer du code absolument calamiteux.
Encore merci à tous,
Olivier.
Olivier Boudeville wrote:
Merci à tous pour vos réponses,
je déduis de tout cela que les trois formes :
- 'const std::string s = "foo";' dans le header
- 'extern const std::string s ;' dans le header + "const
std::string s
= "foo" ;' dans l'implémentation
- 'const std::string & get_s() ;' + sa définition
La quatrième forme
'extern char const s[];' dans le header +
'char const s[] = "foo";' dans l'implémentation ne présente pas
le problème d'ordre d'initialisation des objets globaux qui se
trouvent dans des unités de compilation différentes (cf. § 3.6.2/1)
et nécessite pas l'introduction d'une fonction.
En effet, même si c'est peut-être moins "C++" comme approche.
Finalement mon code en lui-même était correct, mais le passage aux autotools m'avait fait en profiter pour rajouter -fstack-check dans AM_CXXFLAGS, ce qui conduit manifestement au moins certaines versions de g++ à générer du code absolument calamiteux.
Encore merci à tous,
Olivier.
Olivier Boudeville wrote:
Merci à tous pour vos réponses,
je déduis de tout cela que les trois formes :
- 'const std::string s = "foo";' dans le header - 'extern const std::string s ;' dans le header + "const std::string s = "foo" ;' dans l'implémentation - 'const std::string & get_s() ;' + sa définition
La quatrième forme 'extern char const s[];' dans le header + 'char const s[] = "foo";' dans l'implémentation ne présente pas le problème d'ordre d'initialisation des objets globaux qui se trouvent dans des unités de compilation différentes (cf. § 3.6.2/1) et nécessite pas l'introduction d'une fonction.
Falk
Jean-Marc Bourguet
Sylvain writes:
J'ai beaucoup de mal à te suivre. Je croyais que tu cherchais à expliquer quelque chose de plus profond que la la différence entre les fonctions membres virtuelles et les autres, mais après plusieurs lectures, je n'en suis plus sûr du tout.
J'ai au moins un problème de vocabulaire.
Je me souviens qu'on s'était plus ou moins mis d'accord sur les termes français suivants:
- redéfinir (redefine): donner une nouvelle définition d'un identificateur.
- surcharger (overload): donner dans une même portée plusieurs définitions pour un même identificateur. En C++, ce n'est possible que pour les fonctions et les différentes fonctions doivent avoir des prototypes différents.
- supplanter (override): redéfinir dans une classe un membre fonction virtuel d'une de ses bases avec le même prototype.
- substituer (je ne me souviens pas d'un terme en anglais): redéfinir dans une classe un membre fonction non virtuel d'une de ses bases avec le même prototype.
- remplacer (replace): fournir une définition qui remplace celle de la bibliothèque standard (possible uniquement pour ::operator new etc)
- masquer (hide): fait qu'une définition dans une portée empèche de voir les définitions du même identificateur se trouvant dans les portées englobantes.
Tu m'a l'air d'utiliser "surcharger" pour supplanter et "masquer" dans un sens que je n'arrive pas à bien déterminer.
De plus, l'utilisation du mot méthode en C++ est à éviter car ambigu: certains l'utilisent pour tous les membres fonctions (ça m'a l'air d'être ton cas) ou d'autres uniquement ceux qui sont virtuels (ce serait plutôt ma tendance, ce qui complique encore ma lecture).
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
Sylvain <noSpam@mail.net> writes:
J'ai beaucoup de mal à te suivre. Je croyais que tu
cherchais à expliquer quelque chose de plus profond que la
la différence entre les fonctions membres virtuelles et les
autres, mais après plusieurs lectures, je n'en suis plus sûr
du tout.
J'ai au moins un problème de vocabulaire.
Je me souviens qu'on s'était plus ou moins mis d'accord sur
les termes français suivants:
- redéfinir (redefine): donner une nouvelle définition d'un
identificateur.
- surcharger (overload): donner dans une même portée plusieurs
définitions pour un même identificateur. En C++, ce n'est
possible que pour les fonctions et les différentes
fonctions doivent avoir des prototypes différents.
- supplanter (override): redéfinir dans une classe un membre
fonction virtuel d'une de ses bases avec le même
prototype.
- substituer (je ne me souviens pas d'un terme en anglais):
redéfinir dans une classe un membre fonction non virtuel
d'une de ses bases avec le même prototype.
- remplacer (replace): fournir une définition qui remplace
celle de la bibliothèque standard (possible uniquement
pour ::operator new etc)
- masquer (hide): fait qu'une définition dans une portée empèche de
voir les définitions du même identificateur se trouvant
dans les portées englobantes.
Tu m'a l'air d'utiliser "surcharger" pour supplanter et
"masquer" dans un sens que je n'arrive pas à bien
déterminer.
De plus, l'utilisation du mot méthode en C++ est à éviter
car ambigu: certains l'utilisent pour tous les membres
fonctions (ça m'a l'air d'être ton cas) ou d'autres
uniquement ceux qui sont virtuels (ce serait plutôt ma
tendance, ce qui complique encore ma lecture).
A+
--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
J'ai beaucoup de mal à te suivre. Je croyais que tu cherchais à expliquer quelque chose de plus profond que la la différence entre les fonctions membres virtuelles et les autres, mais après plusieurs lectures, je n'en suis plus sûr du tout.
J'ai au moins un problème de vocabulaire.
Je me souviens qu'on s'était plus ou moins mis d'accord sur les termes français suivants:
- redéfinir (redefine): donner une nouvelle définition d'un identificateur.
- surcharger (overload): donner dans une même portée plusieurs définitions pour un même identificateur. En C++, ce n'est possible que pour les fonctions et les différentes fonctions doivent avoir des prototypes différents.
- supplanter (override): redéfinir dans une classe un membre fonction virtuel d'une de ses bases avec le même prototype.
- substituer (je ne me souviens pas d'un terme en anglais): redéfinir dans une classe un membre fonction non virtuel d'une de ses bases avec le même prototype.
- remplacer (replace): fournir une définition qui remplace celle de la bibliothèque standard (possible uniquement pour ::operator new etc)
- masquer (hide): fait qu'une définition dans une portée empèche de voir les définitions du même identificateur se trouvant dans les portées englobantes.
Tu m'a l'air d'utiliser "surcharger" pour supplanter et "masquer" dans un sens que je n'arrive pas à bien déterminer.
De plus, l'utilisation du mot méthode en C++ est à éviter car ambigu: certains l'utilisent pour tous les membres fonctions (ça m'a l'air d'être ton cas) ou d'autres uniquement ceux qui sont virtuels (ce serait plutôt ma tendance, ce qui complique encore ma lecture).
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
Sylvain
Jean-Marc Bourguet wrote on 25/06/2006 18:02:
Sylvain writes:
J'ai beaucoup de mal à te suivre. Je croyais que tu cherchais à expliquer quelque chose de plus profond que la la différence entre les fonctions membres virtuelles et les autres, mais après plusieurs lectures, je n'en suis plus sûr du tout.
tu as bien lu: expliquer un si petite chose en 4 mails et autant de lignes est au moins étonnant surtout pour qui lit des évidences; j'ai aussi eu des doutes sur la question exacte (les pré-supposés) de Fabien. je n'ai pas voulu me contenter de définitions strictes, car qui ne maitrise pas l'impact de virtual, ne reconnaitra peut être pas spontanément une substitution, d'une supplantation. ceci étant, produire ça après 18h devant l'écran était une mauvaise idée.
Je me souviens qu'on s'était plus ou moins mis d'accord sur les termes français suivants:
oui, j'utilise surcharge partout où 2 identifiants ont le même nom, je définirais même la surcharge par ce simple fait; selon d'autres caractéristiques (dont présence dans une même classe ou parent/fille, virtuel ou non etc) je dirais que cette surcharge induit une substitution au autre ... mon 'surcharge ' est donc un raccourci, tes définitions sont plus descriptives.
j'utilise par contre masquer dans le sens que tu as indiqué - sous réserve que l'on parle du point de vue de l'utilisateur de la classe et non de la classe elle-même.
De plus, l'utilisation du mot méthode en C++ est à éviter car ambigu: certains l'utilisent pour tous les membres fonctions (ça m'a l'air d'être ton cas) ou d'autres uniquement ceux qui sont virtuels (ce serait plutôt ma tendance, ce qui complique encore ma lecture).
j'utilise méthode un peu par opposition aux fonctions (globales) du C, Java utilise volontiers méthode, est-ce que la virtualité que tu associe implicitement à méthode vient de là ? dans mon propos 'méthode' n'impliquait pas virtuel; si cela devait être le cas, et reserver function aux non virtuelles, cela m'irait très bien ... mais je doute un peu de ma discipline pour l'appliquer sans faille.
Sylvain.
Jean-Marc Bourguet wrote on 25/06/2006 18:02:
Sylvain <noSpam@mail.net> writes:
J'ai beaucoup de mal à te suivre. Je croyais que tu
cherchais à expliquer quelque chose de plus profond que la
la différence entre les fonctions membres virtuelles et les
autres, mais après plusieurs lectures, je n'en suis plus sûr
du tout.
tu as bien lu: expliquer un si petite chose en 4 mails et autant de
lignes est au moins étonnant surtout pour qui lit des évidences; j'ai
aussi eu des doutes sur la question exacte (les pré-supposés) de Fabien.
je n'ai pas voulu me contenter de définitions strictes, car qui ne
maitrise pas l'impact de virtual, ne reconnaitra peut être pas
spontanément une substitution, d'une supplantation.
ceci étant, produire ça après 18h devant l'écran était une mauvaise idée.
Je me souviens qu'on s'était plus ou moins mis d'accord sur
les termes français suivants:
oui, j'utilise surcharge partout où 2 identifiants ont le même nom, je
définirais même la surcharge par ce simple fait; selon d'autres
caractéristiques (dont présence dans une même classe ou parent/fille,
virtuel ou non etc) je dirais que cette surcharge induit une
substitution au autre ... mon 'surcharge ' est donc un raccourci, tes
définitions sont plus descriptives.
j'utilise par contre masquer dans le sens que tu as indiqué - sous
réserve que l'on parle du point de vue de l'utilisateur de la classe et
non de la classe elle-même.
De plus, l'utilisation du mot méthode en C++ est à éviter
car ambigu: certains l'utilisent pour tous les membres
fonctions (ça m'a l'air d'être ton cas) ou d'autres
uniquement ceux qui sont virtuels (ce serait plutôt ma
tendance, ce qui complique encore ma lecture).
j'utilise méthode un peu par opposition aux fonctions (globales) du C,
Java utilise volontiers méthode, est-ce que la virtualité que tu associe
implicitement à méthode vient de là ?
dans mon propos 'méthode' n'impliquait pas virtuel; si cela devait être
le cas, et reserver function aux non virtuelles, cela m'irait très bien
... mais je doute un peu de ma discipline pour l'appliquer sans faille.
J'ai beaucoup de mal à te suivre. Je croyais que tu cherchais à expliquer quelque chose de plus profond que la la différence entre les fonctions membres virtuelles et les autres, mais après plusieurs lectures, je n'en suis plus sûr du tout.
tu as bien lu: expliquer un si petite chose en 4 mails et autant de lignes est au moins étonnant surtout pour qui lit des évidences; j'ai aussi eu des doutes sur la question exacte (les pré-supposés) de Fabien. je n'ai pas voulu me contenter de définitions strictes, car qui ne maitrise pas l'impact de virtual, ne reconnaitra peut être pas spontanément une substitution, d'une supplantation. ceci étant, produire ça après 18h devant l'écran était une mauvaise idée.
Je me souviens qu'on s'était plus ou moins mis d'accord sur les termes français suivants:
oui, j'utilise surcharge partout où 2 identifiants ont le même nom, je définirais même la surcharge par ce simple fait; selon d'autres caractéristiques (dont présence dans une même classe ou parent/fille, virtuel ou non etc) je dirais que cette surcharge induit une substitution au autre ... mon 'surcharge ' est donc un raccourci, tes définitions sont plus descriptives.
j'utilise par contre masquer dans le sens que tu as indiqué - sous réserve que l'on parle du point de vue de l'utilisateur de la classe et non de la classe elle-même.
De plus, l'utilisation du mot méthode en C++ est à éviter car ambigu: certains l'utilisent pour tous les membres fonctions (ça m'a l'air d'être ton cas) ou d'autres uniquement ceux qui sont virtuels (ce serait plutôt ma tendance, ce qui complique encore ma lecture).
j'utilise méthode un peu par opposition aux fonctions (globales) du C, Java utilise volontiers méthode, est-ce que la virtualité que tu associe implicitement à méthode vient de là ? dans mon propos 'méthode' n'impliquait pas virtuel; si cela devait être le cas, et reserver function aux non virtuelles, cela m'irait très bien ... mais je doute un peu de ma discipline pour l'appliquer sans faille.
Sylvain.
Fabien LE LEZ
On 25 Jun 2006 08:41:13 +0200, Jean-Marc Bourguet :
Les trois méthodes ont un problème si la variable est utilisée durant la destruction d'autres variables statiques qui auront été construite avant elle.
Il y a une quatrième méthode, plus bourrin et moins optimisée, mais qui n'a pas ce problème : std::string get_s();
On 25 Jun 2006 08:41:13 +0200, Jean-Marc Bourguet <jm@bourguet.org>:
Les trois méthodes ont un problème si la variable est
utilisée durant la destruction d'autres variables statiques
qui auront été construite avant elle.
Il y a une quatrième méthode, plus bourrin et moins optimisée, mais
qui n'a pas ce problème :
std::string get_s();
On 25 Jun 2006 08:41:13 +0200, Jean-Marc Bourguet :
Les trois méthodes ont un problème si la variable est utilisée durant la destruction d'autres variables statiques qui auront été construite avant elle.
Il y a une quatrième méthode, plus bourrin et moins optimisée, mais qui n'a pas ce problème : std::string get_s();
James Kanze
Fabien LE LEZ wrote:
On Sat, 24 Jun 2006 12:33:05 +0200, Olivier Boudeville :
C'est correct, à condition de ne pas se servir de Ceylan::URI::ProtocolSeparator dans le constructeur d'un objet à la portée namespace.
En tout cas, ceci est correct :
namespace Ceylan { namespace URI { const std::string ProtocolSeparator= "://";
Oui. Mais ça donne autant d'objets ProtocolSeparator qu'il y a d'unités de traduction.
est un objet qui serait à construire avant la première instruction de main
C'est assez courant.
Dans la pratique, c'est la seule solution utilisée -- dans le cas des objets linkés statiquement. Dans les cas d'une édition de liens dynamique, d'autres règles peuvent s'appliquer.
-- James Kanze 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
Fabien LE LEZ wrote:
On Sat, 24 Jun 2006 12:33:05 +0200, Olivier Boudeville
<olivier.boudevilleNOSPAM@online.fr>:
C'est correct, à condition de ne pas se servir de
Ceylan::URI::ProtocolSeparator dans le constructeur d'un objet à
la portée namespace.
En tout cas, ceci est correct :
namespace Ceylan
{
namespace URI
{
const std::string ProtocolSeparator= "://";
Oui. Mais ça donne autant d'objets ProtocolSeparator qu'il y a
d'unités de traduction.
est un objet qui serait à construire avant la première
instruction de main
C'est assez courant.
Dans la pratique, c'est la seule solution utilisée -- dans le
cas des objets linkés statiquement. Dans les cas d'une édition
de liens dynamique, d'autres règles peuvent s'appliquer.
--
James Kanze kanze.james@neuf.fr
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
C'est correct, à condition de ne pas se servir de Ceylan::URI::ProtocolSeparator dans le constructeur d'un objet à la portée namespace.
En tout cas, ceci est correct :
namespace Ceylan { namespace URI { const std::string ProtocolSeparator= "://";
Oui. Mais ça donne autant d'objets ProtocolSeparator qu'il y a d'unités de traduction.
est un objet qui serait à construire avant la première instruction de main
C'est assez courant.
Dans la pratique, c'est la seule solution utilisée -- dans le cas des objets linkés statiquement. Dans les cas d'une édition de liens dynamique, d'autres règles peuvent s'appliquer.
-- James Kanze 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
James Kanze
Sylvain wrote:
Fabien LE LEZ wrote on 24/06/2006 21:31:
On Sat, 24 Jun 2006 21:05:18 +0200, Olivier Boudeville :
Si je n'utilise pas cette variable "ProtocolSeparator" en argument d'une autre construction de type classe-statique ou extern, i.e. si, a priori, sa première utilisation est à l'intérieur de main(), alors (je m'étais dit que) on est assuré que toutes les constructions situées en dehors de toute fonction seraient achevées à l'entrée dans main().
Si je ne m'abuse, c'est bien le cas. Le problème étant de s'assurer qu'aucun code n'utilise cette variable avant le début de main().
c'est le cas individuel (chaque object static est construit à l'entrée de main) mais il n'y a pas de règle d'ensemble: chacun de ces objects statiques est contruit dans un ordre imprévisible.
il peut donc y avoir crash si un objet static s'initialise à partir d'autres d'objects static (pouvant ou non être alors construits).
Juste un petit détail, mais l'ordre est garanti à l'intérieur d'une unité de traduction.
je suppose que ProtocolSeparator est utilisé par d'autre cst statique pour créer d'autre URI; si c'est le cas, sa substitution par une fonction est requise - et je rejoins l'avis de Fabien sur ce point, la fonction n'a pas de lourdeur intrinsèque et garantira l'initialisation de ton object.
Ce qui n'est pas forcément sans problèmes dans le cas d'un environement multi-threaded.
-- James Kanze 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
Sylvain wrote:
Fabien LE LEZ wrote on 24/06/2006 21:31:
On Sat, 24 Jun 2006 21:05:18 +0200, Olivier Boudeville
<olivier.boudevilleNOSPAM@online.fr>:
Si je n'utilise pas cette variable "ProtocolSeparator" en
argument d'une autre construction de type classe-statique ou
extern, i.e. si, a priori, sa première utilisation est à
l'intérieur de main(), alors (je m'étais dit que) on est
assuré que toutes les constructions situées en dehors de
toute fonction seraient achevées à l'entrée dans main().
Si je ne m'abuse, c'est bien le cas. Le problème étant de
s'assurer qu'aucun code n'utilise cette variable avant le
début de main().
c'est le cas individuel (chaque object static est construit à
l'entrée de main) mais il n'y a pas de règle d'ensemble:
chacun de ces objects statiques est contruit dans un ordre
imprévisible.
il peut donc y avoir crash si un objet static s'initialise à
partir d'autres d'objects static (pouvant ou non être alors
construits).
Juste un petit détail, mais l'ordre est garanti à l'intérieur
d'une unité de traduction.
je suppose que ProtocolSeparator est utilisé par d'autre cst
statique pour créer d'autre URI; si c'est le cas, sa
substitution par une fonction est requise - et je rejoins
l'avis de Fabien sur ce point, la fonction n'a pas de lourdeur
intrinsèque et garantira l'initialisation de ton object.
Ce qui n'est pas forcément sans problèmes dans le cas d'un
environement multi-threaded.
--
James Kanze kanze.james@neuf.fr
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
On Sat, 24 Jun 2006 21:05:18 +0200, Olivier Boudeville :
Si je n'utilise pas cette variable "ProtocolSeparator" en argument d'une autre construction de type classe-statique ou extern, i.e. si, a priori, sa première utilisation est à l'intérieur de main(), alors (je m'étais dit que) on est assuré que toutes les constructions situées en dehors de toute fonction seraient achevées à l'entrée dans main().
Si je ne m'abuse, c'est bien le cas. Le problème étant de s'assurer qu'aucun code n'utilise cette variable avant le début de main().
c'est le cas individuel (chaque object static est construit à l'entrée de main) mais il n'y a pas de règle d'ensemble: chacun de ces objects statiques est contruit dans un ordre imprévisible.
il peut donc y avoir crash si un objet static s'initialise à partir d'autres d'objects static (pouvant ou non être alors construits).
Juste un petit détail, mais l'ordre est garanti à l'intérieur d'une unité de traduction.
je suppose que ProtocolSeparator est utilisé par d'autre cst statique pour créer d'autre URI; si c'est le cas, sa substitution par une fonction est requise - et je rejoins l'avis de Fabien sur ce point, la fonction n'a pas de lourdeur intrinsèque et garantira l'initialisation de ton object.
Ce qui n'est pas forcément sans problèmes dans le cas d'un environement multi-threaded.
-- James Kanze 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
James Kanze
Olivier Boudeville wrote:
je déduis de tout cela que les trois formes :
- 'const std::string s = "foo";' dans le header - 'extern const std::string s ;' dans le header + "const std::string s = "foo" ;' dans l'implémentation - 'const std::string & get_s() ;' + sa définition
sont correctes, et que la troisième est probablement à privilégier en général.
Dans un environement single-threaded.
À vrai dire, dans un cas comme ceci, la solution à privilégier en général est « char const s[] ». Parce qu'elle permet une initialisation statique, elle contourne d'office tous les problèmes d'ordre d'initialisation. Évidemment, chaque fois qu'on a besoin de la chaîne en tant que std::string. on va le construire de nouveau.
-- James Kanze 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
Olivier Boudeville wrote:
je déduis de tout cela que les trois formes :
- 'const std::string s = "foo";' dans le header
- 'extern const std::string s ;' dans le header + "const std::string s
= "foo" ;' dans l'implémentation
- 'const std::string & get_s() ;' + sa définition
sont correctes, et que la troisième est probablement à
privilégier en général.
Dans un environement single-threaded.
À vrai dire, dans un cas comme ceci, la solution à privilégier
en général est « char const s[] ». Parce qu'elle permet une
initialisation statique, elle contourne d'office tous les
problèmes d'ordre d'initialisation. Évidemment, chaque fois
qu'on a besoin de la chaîne en tant que std::string. on va le
construire de nouveau.
--
James Kanze kanze.james@neuf.fr
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
- 'const std::string s = "foo";' dans le header - 'extern const std::string s ;' dans le header + "const std::string s = "foo" ;' dans l'implémentation - 'const std::string & get_s() ;' + sa définition
sont correctes, et que la troisième est probablement à privilégier en général.
Dans un environement single-threaded.
À vrai dire, dans un cas comme ceci, la solution à privilégier en général est « char const s[] ». Parce qu'elle permet une initialisation statique, elle contourne d'office tous les problèmes d'ordre d'initialisation. Évidemment, chaque fois qu'on a besoin de la chaîne en tant que std::string. on va le construire de nouveau.
-- James Kanze 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
James Kanze
Jean-Marc Bourguet wrote:
Sylvain writes:
Si je ne m'abuse, c'est bien le cas. Le problème étant de s'assurer qu'aucun code n'utilise cette variable avant le début de main().
c'est le cas individuel (chaque object static est construit à l'entrée de main)
Ca dépend de l'implémentation. La seule contrainte c'est que si elles sont postposées après le début de l'exécution de main, elles doivent avoir lieu avant la première exécution d'une fonction définie dans leur unité de compilation.
Avant la première utilisation d'un objet dans l'unité de compilation. Je crois (sans l'avoir vérifié) que même en prendre l'adresse d'un objet global dans l'unité de traduction suffit d'assurer la construction. Selon la norme, s'entend.
Évidemment, tel que c'est écrit dans la norme, c'est impossible à implémenter, parce que la norme ne fait pas d'exception pour des utilisations cycliques. (Si l'initialisation a lieu avant l'appel de main, l'obligation d'initialisation avant l'utilisation ne vaut plus, et il n'y a donc plus de problème pour l'implémentation.)
Dans la pratique, on peut très bien compter sur l'initialisation avant main.
(L'idée est de permettre de postposer le chargement d'une bibliothèque dynamique à sa première utilisation.)
C'était effectivement la motivation. Seulement, étant donné que le chargement d'un bibliothèque dynamique viole d'autres règles, comme les phases de traduction, on est déjà dans le cas du comportement indéfini.
mais il n'y a pas de règle d'ensemble: chacun de ces objects statiques est contruit dans un ordre imprévisible.
Il faut discerner les initialisations avec des valeurs constantes (appelées initialisations statiques) et les autres (appelées initialisations dynamiques).
L'initialisation des variables statiques se fait en trois temps:
Attention au langage ici. Quand tu parles des « variables statiques » ci-dessus, il s'agit des variables à durée de vie statique, et non des variables à initialisation statique. (Ton utilisation correspond bien à une racourcie courante, mais puisqu'on parlait ici tellement d'initialisation...)
- d'abord une initialisation à 0 (qui n'est pas un memset(&v, 0, sizeof v) mais un v = 0).
Et qui ne concerne pas les références, et que le premier membre d'une union.
- ensuite toutes les initialisation statiques (en pratique ces deux phases ont lieu en même temps, un programme conforme ne pouvant pas voir la différence).
- ensuite les initialisations dynamiques. A l'intérieur d'une unité de compilation, elles se font dans l'ordre d'apparition des définitions.
Seulement entre les variables à portée référencielle. Une variable statique à portée de bloque serait initialisée la première fois que le flux d'exécution le rencontre. (Dynamiquement. L'initialisation à 0 a toujours lieu avant, et évidemment, si c'est une initialisation statique, le programme ne peut pas savoir, et dans la pratique, l'initialisation a lieu au chargement du programme, comme pour les autres variables à initialisation statique.)
Donc il y a donc trois ordres prévisibles (les initialisations statiques sont faites avant toutes initialisations dynamiques, à l'intérieur d'une unité de compilation les initialisations dynamiques sont ordonnées, on sait que les objets ayant une initialisation dynamique seront au moins initialisés à 0).
Ce dernier peut parfois être bien utile, et il y a un ou deux idiomes qui y comptent, même si c'est un peu subtile.
-- James Kanze 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
Jean-Marc Bourguet wrote:
Sylvain <noSpam@mail.net> writes:
Si je ne m'abuse, c'est bien le cas. Le problème étant de
s'assurer qu'aucun code n'utilise cette variable avant le
début de main().
c'est le cas individuel (chaque object static est construit à
l'entrée de main)
Ca dépend de l'implémentation. La seule contrainte c'est que
si elles sont postposées après le début de l'exécution de
main, elles doivent avoir lieu avant la première exécution
d'une fonction définie dans leur unité de compilation.
Avant la première utilisation d'un objet dans l'unité de
compilation. Je crois (sans l'avoir vérifié) que même en prendre
l'adresse d'un objet global dans l'unité de traduction suffit
d'assurer la construction. Selon la norme, s'entend.
Évidemment, tel que c'est écrit dans la norme, c'est impossible
à implémenter, parce que la norme ne fait pas d'exception pour
des utilisations cycliques. (Si l'initialisation a lieu avant
l'appel de main, l'obligation d'initialisation avant
l'utilisation ne vaut plus, et il n'y a donc plus de problème
pour l'implémentation.)
Dans la pratique, on peut très bien compter sur l'initialisation
avant main.
(L'idée est de permettre de postposer le chargement d'une
bibliothèque dynamique à sa première utilisation.)
C'était effectivement la motivation. Seulement, étant donné que
le chargement d'un bibliothèque dynamique viole d'autres règles,
comme les phases de traduction, on est déjà dans le cas du
comportement indéfini.
mais il n'y a pas de règle d'ensemble: chacun de ces objects
statiques est contruit dans un ordre imprévisible.
Il faut discerner les initialisations avec des valeurs
constantes (appelées initialisations statiques) et les autres
(appelées initialisations dynamiques).
L'initialisation des variables statiques se fait en trois
temps:
Attention au langage ici. Quand tu parles des « variables
statiques » ci-dessus, il s'agit des variables à durée de vie
statique, et non des variables à initialisation statique. (Ton
utilisation correspond bien à une racourcie courante, mais
puisqu'on parlait ici tellement d'initialisation...)
- d'abord une initialisation à 0 (qui n'est pas un
memset(&v, 0, sizeof v) mais un v = 0).
Et qui ne concerne pas les références, et que le premier membre
d'une union.
- ensuite toutes les initialisation statiques (en pratique
ces deux phases ont lieu en même temps, un programme
conforme ne pouvant pas voir la différence).
- ensuite les initialisations dynamiques. A l'intérieur
d'une unité de compilation, elles se font dans l'ordre
d'apparition des définitions.
Seulement entre les variables à portée référencielle. Une
variable statique à portée de bloque serait initialisée la
première fois que le flux d'exécution le rencontre.
(Dynamiquement. L'initialisation à 0 a toujours lieu avant, et
évidemment, si c'est une initialisation statique, le programme
ne peut pas savoir, et dans la pratique, l'initialisation a lieu
au chargement du programme, comme pour les autres variables à
initialisation statique.)
Donc il y a donc trois ordres prévisibles (les initialisations
statiques sont faites avant toutes initialisations dynamiques,
à l'intérieur d'une unité de compilation les initialisations
dynamiques sont ordonnées, on sait que les objets ayant une
initialisation dynamique seront au moins initialisés à 0).
Ce dernier peut parfois être bien utile, et il y a un ou deux
idiomes qui y comptent, même si c'est un peu subtile.
--
James Kanze kanze.james@neuf.fr
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
Si je ne m'abuse, c'est bien le cas. Le problème étant de s'assurer qu'aucun code n'utilise cette variable avant le début de main().
c'est le cas individuel (chaque object static est construit à l'entrée de main)
Ca dépend de l'implémentation. La seule contrainte c'est que si elles sont postposées après le début de l'exécution de main, elles doivent avoir lieu avant la première exécution d'une fonction définie dans leur unité de compilation.
Avant la première utilisation d'un objet dans l'unité de compilation. Je crois (sans l'avoir vérifié) que même en prendre l'adresse d'un objet global dans l'unité de traduction suffit d'assurer la construction. Selon la norme, s'entend.
Évidemment, tel que c'est écrit dans la norme, c'est impossible à implémenter, parce que la norme ne fait pas d'exception pour des utilisations cycliques. (Si l'initialisation a lieu avant l'appel de main, l'obligation d'initialisation avant l'utilisation ne vaut plus, et il n'y a donc plus de problème pour l'implémentation.)
Dans la pratique, on peut très bien compter sur l'initialisation avant main.
(L'idée est de permettre de postposer le chargement d'une bibliothèque dynamique à sa première utilisation.)
C'était effectivement la motivation. Seulement, étant donné que le chargement d'un bibliothèque dynamique viole d'autres règles, comme les phases de traduction, on est déjà dans le cas du comportement indéfini.
mais il n'y a pas de règle d'ensemble: chacun de ces objects statiques est contruit dans un ordre imprévisible.
Il faut discerner les initialisations avec des valeurs constantes (appelées initialisations statiques) et les autres (appelées initialisations dynamiques).
L'initialisation des variables statiques se fait en trois temps:
Attention au langage ici. Quand tu parles des « variables statiques » ci-dessus, il s'agit des variables à durée de vie statique, et non des variables à initialisation statique. (Ton utilisation correspond bien à une racourcie courante, mais puisqu'on parlait ici tellement d'initialisation...)
- d'abord une initialisation à 0 (qui n'est pas un memset(&v, 0, sizeof v) mais un v = 0).
Et qui ne concerne pas les références, et que le premier membre d'une union.
- ensuite toutes les initialisation statiques (en pratique ces deux phases ont lieu en même temps, un programme conforme ne pouvant pas voir la différence).
- ensuite les initialisations dynamiques. A l'intérieur d'une unité de compilation, elles se font dans l'ordre d'apparition des définitions.
Seulement entre les variables à portée référencielle. Une variable statique à portée de bloque serait initialisée la première fois que le flux d'exécution le rencontre. (Dynamiquement. L'initialisation à 0 a toujours lieu avant, et évidemment, si c'est une initialisation statique, le programme ne peut pas savoir, et dans la pratique, l'initialisation a lieu au chargement du programme, comme pour les autres variables à initialisation statique.)
Donc il y a donc trois ordres prévisibles (les initialisations statiques sont faites avant toutes initialisations dynamiques, à l'intérieur d'une unité de compilation les initialisations dynamiques sont ordonnées, on sait que les objets ayant une initialisation dynamique seront au moins initialisés à 0).
Ce dernier peut parfois être bien utile, et il y a un ou deux idiomes qui y comptent, même si c'est un peu subtile.
-- James Kanze 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
Fabien LE LEZ
On Sun, 25 Jun 2006 23:31:10 +0200, James Kanze :
En tout cas, ceci est correct :
namespace Ceylan { namespace URI { const std::string ProtocolSeparator= "://";
Oui. Mais ça donne autant d'objets ProtocolSeparator qu'il y a d'unités de traduction.
Uh ? Si ces lignes sont dans un .cpp, et la ligne "extern..." dans le .h, on n'a qu'une variable (globale), non ?
On Sun, 25 Jun 2006 23:31:10 +0200, James Kanze <kanze.james@neuf.fr>:
En tout cas, ceci est correct :
namespace Ceylan
{
namespace URI
{
const std::string ProtocolSeparator= "://";
Oui. Mais ça donne autant d'objets ProtocolSeparator qu'il y a
d'unités de traduction.
Uh ?
Si ces lignes sont dans un .cpp, et la ligne "extern..." dans le .h,
on n'a qu'une variable (globale), non ?
namespace Ceylan { namespace URI { const std::string ProtocolSeparator= "://";
Oui. Mais ça donne autant d'objets ProtocolSeparator qu'il y a d'unités de traduction.
Uh ? Si ces lignes sont dans un .cpp, et la ligne "extern..." dans le .h, on n'a qu'une variable (globale), non ?
James Kanze
Jean-Marc Bourguet wrote:
Je me souviens qu'on s'était plus ou moins mis d'accord sur les termes français suivants:
Dans ce groupe, au moins. Plus généralement, je crois que...
- redéfinir (redefine): donner une nouvelle définition d'un identificateur.
[...]
- supplanter (override): redéfinir dans une classe un membre fonction virtuel d'une de ses bases avec le même prototype.
On voit redéfinir pour cette signification aussi (même s'il y a ambiguïté avec ta première définition.
[...]
De plus, l'utilisation du mot méthode en C++ est à éviter car ambigu: certains l'utilisent pour tous les membres fonctions (ça m'a l'air d'être ton cas) ou d'autres uniquement ceux qui sont virtuels (ce serait plutôt ma tendance, ce qui complique encore ma lecture).
Sans parler de sa signification plus générale, par exemple, la méthode utilisée pour résoudre un problème.
-- James Kanze 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
Jean-Marc Bourguet wrote:
Je me souviens qu'on s'était plus ou moins mis d'accord sur
les termes français suivants:
Dans ce groupe, au moins. Plus généralement, je crois que...
- redéfinir (redefine): donner une nouvelle définition d'un
identificateur.
[...]
- supplanter (override): redéfinir dans une classe un membre
fonction virtuel d'une de ses bases avec le même
prototype.
On voit redéfinir pour cette signification aussi (même s'il y a
ambiguïté avec ta première définition.
[...]
De plus, l'utilisation du mot méthode en C++ est à éviter
car ambigu: certains l'utilisent pour tous les membres
fonctions (ça m'a l'air d'être ton cas) ou d'autres
uniquement ceux qui sont virtuels (ce serait plutôt ma
tendance, ce qui complique encore ma lecture).
Sans parler de sa signification plus générale, par exemple, la
méthode utilisée pour résoudre un problème.
--
James Kanze kanze.james@neuf.fr
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
Je me souviens qu'on s'était plus ou moins mis d'accord sur les termes français suivants:
Dans ce groupe, au moins. Plus généralement, je crois que...
- redéfinir (redefine): donner une nouvelle définition d'un identificateur.
[...]
- supplanter (override): redéfinir dans une classe un membre fonction virtuel d'une de ses bases avec le même prototype.
On voit redéfinir pour cette signification aussi (même s'il y a ambiguïté avec ta première définition.
[...]
De plus, l'utilisation du mot méthode en C++ est à éviter car ambigu: certains l'utilisent pour tous les membres fonctions (ça m'a l'air d'être ton cas) ou d'autres uniquement ceux qui sont virtuels (ce serait plutôt ma tendance, ce qui complique encore ma lecture).
Sans parler de sa signification plus générale, par exemple, la méthode utilisée pour résoudre un problème.
-- James Kanze 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