OVH Cloud OVH Cloud

Déclaration de chaînes constantes

43 réponses
Avatar
Olivier Boudeville
Bonjour à tous,

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 :

namespace Ceylan
{
namespace URI
{
[..]
extern const std::string ProtocolSeparator ;
[..]

et dans CeylanUniformResourceIdentifier.cc (un des fichiers qui inclut
CeylanUniformResourceIdentifier.h) je le définis ainsi :

const std::string Ceylan::URI::ProtocolSeparator = "://" ;

Est-ce que c'est une pratique correcte ?

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 :

const std::string& ProtocolSeparator()
{
static const std::string instance = "://";
return instance;
}

) 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)

Merci d'avance !

Olivier.

10 réponses

1 2 3 4 5
Avatar
Gabriel Dos Reis
Sylvain writes:

[...]

| ceci étant, produire ça après 18h devant l'écran était une mauvaise idée.

C'est plus 35h en France ? ;-p

-- Gaby
Avatar
Gabriel Dos Reis
James Kanze writes:

| 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.

As-tu un passage particulier de la norme en tête ?

-- Gaby
Avatar
kanze
Fabien LE LEZ wrote:
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 ?


Oui. Je croyais que tu proposais un alternatif à son en-tête.

--
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
Jean-Marc Bourguet
James Kanze writes:

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.


J'ai relu les passages qui me semblaient etre pertinents avant de
repondre. As-tu un pointeur vers celui que j'ai manque?

[...]
(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.


Quels sont les regles violees si je fais:

g++ -fPIC -c foo.cpp
g++ -shared -o foo.so foo.o
g++ -o bar bar.cpp foo.so
./bar

En initialisant les statiques de foo uniquement lors d'un appel a une
fonction de l'unite de compilation. Ce n'est pas ce qui se passe avec
Unix, nous sommes d'accord, mais il me semblait que c'etait
l'utilisation pour laquelle la latitude est laissee (par opposition a
une utilisation ou un dlopen explicite charge une bibliotheque
dynamique ou l'initialisation des variables a duree de vie statique a
lieu a ce moment).

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




Avatar
kanze
Gabriel Dos Reis wrote:
James Kanze writes:

| 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.

As-tu un passage particulier de la norme en tête ?


Pas vraiment. Je ne me suis jamais vraiment penché sur la
question de ce qui constitue une « utilisation » dans ce
cas-ci ; dans la pratique, je compte sur une initialisation
avant d'entrer dans main. Il y a bien §3.2/2 : « An object or
non-overloaded function is used if its name appears in a
potentially-evaluated expression. » Mais je ne sais pas si
c'est l'intention que cette définition s'applique dans §3.6.2.
C'une côté, je ne vois rien d'autre. Mais de l'autre... en
prendre l'adresse, c'est une expression constante, qui pourrait
donc avoir lieu dans une initialisation statique. Et c'est clair
que le constructeur ne peut pas être appelé avant les
initialisations statiques. (Mais les initialisations statiques
ont lieu bien avant l'entrée en main. Elles ne sont donc pas
concernées par cette règle.)

--
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
Gabriel Dos Reis wrote:
Sylvain writes:

[...]

ceci étant, produire ça après 18h devant l'écran était une
mauvaise idée.


C'est plus 35h en France ? ;-p


Les ingenieurs, ce sont des cadres et assimilés. C-à-d qu'il ne
pointe pas, que la direction decrète ce que représente 35 heures
de travail, et que c'est ça, ses 35 heures, même s'il lui en
faut 70.

C'est un concepte assez universel : si jamais il t'arrive de
travailler dans une boîte dans une ancienne industrie aux
États-unis (genre automobile, etc.), en tant que cadre, tu
serais « exempt », c-à-d pas couvert par les accords syndicaux
sur le temps de travail, le payement des heures supplémentaires,
etc. (Dans les boîtes plus récentes, il n'y a pas d'accords
syndicaux, et vue qu'il n'y a pas de législation du travail non
plus, ils n'ont pas besoin d'une catégorie spéciale.) En
Allemagne, il y a les « AT », les « Außer Tarif »
(litéralement, hors grille), ce qui signifie à peu près la même
chose : les heures supplémentaires ne sont plus payées.

--
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
Jean-Marc Bourguet wrote:
Fabien LE LEZ writes:

On 24 Jun 2006 21:48:06 +0200, Jean-Marc Bourguet
:

static std::string const ma_chaine ("://");


const implique static si il n'y a pas une déclaration
antérieure qui indique que c'est avec un "linkage" externe.


Même si le "static" est de toutes façons implicite, ne
devrait-on pas le mettre explicitement ?


C'est une question de style à mon avis, pas de bonne
pratique.


Plus ou moins. Tu connais mon motto : « Say what you mean, and
mean what you say. » Si tu sais qu'il y aurait autant
d'instances que d'inclusions du fichier, n'est-ce pas
l'obfuscation de ne pas le dire à celui qui lit le code.

Note que dans le cas comme ici, où l'initialisation n'est pas
statique, ça peut avoir un effet sécondaire : tu charges tout
le programme au démarrage, et non seulement au fur et à mesure
qu'on exécute. Parce que tu as du code à exécuter au démarrage
dans chaque unité de traduction qui a inclu l'en-tête. La
plupart du temps, ça ne fait rien, mais dans certains cas, où le
système n'a pas beaucoup de mémoire, ça peut jouer des tours, et
provoquer des swaps (y compris dans d'autres applications) qui
n'aurait pas lieu autrement.

Du coup, je n'utilise pour ainsi dire jamais des const
non-extern des types dont le constructeur n'est pas trivial ;
en fait, c'est assez rare que je m'en sers pour autre chose
qu'un type entier.

Je voulais surtout faire remarquer que le cas n'était en rien
différent de ce qui avait déjà été suggéré.

De même, dans le code

struct A { virtual void f(); }
struct B { (virtual) void f(); }

le "virtual" est implicite ; doit-on tout de même l'écrire
explicitement ?


A nouveau, pour moi c'est une affaire de style.


Oui, mais c'est quand même plus qu'une simple question de
goût ; il y a des arguments concrets et réels pour préférer un
style plutôt que l'autre. (Quand je pense à une affaire de
style, je pense plutôt à des choses comme la position des
accolades ou les conventions de nommage, où il y a plusieurs
alternatifs qui sont en fin de compte également bien, ou
prèsque.)

--
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
Jean-Marc Bourguet
"kanze" writes:

Jean-Marc Bourguet wrote:
Fabien LE LEZ writes:

On 24 Jun 2006 21:48:06 +0200, Jean-Marc Bourguet
:

static std::string const ma_chaine ("://");


const implique static si il n'y a pas une déclaration
antérieure qui indique que c'est avec un "linkage" externe.


Même si le "static" est de toutes façons implicite, ne
devrait-on pas le mettre explicitement ?


C'est une question de style à mon avis, pas de bonne
pratique.


Plus ou moins. Tu connais mon motto : « Say what you mean, and mean
what you say. » Si tu sais qu'il y aurait autant d'instances que
d'inclusions du fichier, n'est-ce pas l'obfuscation de ne pas le
dire à celui qui lit le code.


Je suppose que celui qui lit le code connais assez le C++ pour le
savoir.

Je vois rarement le static mis pour des int par exemple. Et si on ne
le mets pas pour les int, pourquoi le mettre pour les autres types.
Mais bon, des variables globales, const ou pas, c'est relativement
rare.

Du coup, je n'utilise pour ainsi dire jamais des const non-extern
des types dont le constructeur n'est pas trivial ; en fait, c'est
assez rare que je m'en sers pour autre chose qu'un type entier.


Meme chose pour moi.

Je voulais surtout faire remarquer que le cas n'était en rien
différent de ce qui avait déjà été suggéré.

De même, dans le code

struct A { virtual void f(); }
struct B { (virtual) void f(); }

le "virtual" est implicite ; doit-on tout de même l'écrire
explicitement ?


A nouveau, pour moi c'est une affaire de style.


Oui, mais c'est quand même plus qu'une simple question de
goût ; il y a des arguments concrets et réels pour préférer un
style plutôt que l'autre.


Quels sont tes arguments?

Dans les deux cas, j'ai tendance a osciller entre un point de vue et
l'autre, ce qui me semble etre typique de mon attitude face a un
probleme stylistique -- cas du static redondant avec const --, ou d'un
probleme qui n'a pas de bonne solution en restant dans le langage --
cas du virtual.

Parce que moi je vois trois choses a exprimer, et le C++ ne me donne
que deux syntaxes et en plus pour une des choses il me laisse le choix
entre les deux syntaxes ce qui fait qu'il n'y a jamais une
verification statique que ce que je voulais dire est bien ce qui se
passe et un changement dans une base peut me faire basculer d'une
chose a l'autre.

On pourrait dire que savoir qu'un membre est virtuel est important,
mais savoir ou il est introduit est encore plus important parce que
c'est ce qui determine le contrat qu'il doit respecter. On va de
toute facon dependre sur des commentaires ou la connaissance de la
hierarchie pour savoir dans lequel des trois figures on se trouve.

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





Avatar
Miguel Moquillon
Bonjour,

Comme ce post a été crossposté sur fr.comp.objet, je me permet d'y mettre
mon grain de sel.
D'abord, en C++, les méthodes d'une classe ne peuvent être raffinées par une
classe fille. Pour que l'implémentation d'une méthode puisse être redéfinie
dans les classes filles, cette dernière doit alors être marquée
comme 'virtual'. Pourquoi ce mot-clé ? Tout simplement parce que le
compilateur va générer alors une table ... virtuelle (d'où le mot clé
virtual) pour la classe avec dans chacune une association identifiant de
méthode - code d'implémentation ; ou dans le jargon objet association
intitulé du message - méthode. Pourquoi ceci ? pour des raisons
d'optimisation : la table virtuelle ajoute une indirection dans l'appel
d'une méthode et donc est plus coûteuse qu'un appel de méthode directe (non
redéfinie). Donc, et au contraire de la philosophie C, l'optimisation à ce
niveau est par défaut activé et si on souhaite le désactiver, il faut le
préciser.
Bien sûr, du point de vue de la conception, cette caractéristique peut-être
utilisée pour empêcher des classes filles de redéfinir des méthodes
publiques ou protégées, même si cela va au contraire des principes objets.
C'est la dictature des classes mères (ou plus exactement du concepteur des
classes mères).

En Java, c'est l'inverse effectivement : les méthodes sont par défaut
redéfinissable par les classes filles (donc conforme aux principes objets).
Par contre, l'usage du mot-clé 'final' permet d'empêcher la redéfinition.

Miguel
Avatar
Gabriel Dos Reis
"kanze" writes:

| Gabriel Dos Reis wrote:
| > Sylvain writes:
|
| > [...]
|
| > > ceci étant, produire ça après 18h devant l'écran était une
| > > mauvaise idée.
|
| > C'est plus 35h en France ? ;-p
|
| Les ingenieurs, ce sont des cadres et assimilés. C-à-d qu'il ne
| pointe pas, que la direction decrète ce que représente 35 heures
| de travail, et que c'est ça, ses 35 heures, même s'il lui en
| faut 70.
|
| C'est un concepte assez universel : si jamais il t'arrive de
| travailler dans une boîte dans une ancienne industrie aux
| États-unis (genre automobile, etc.), en tant que cadre, tu
| serais « exempt », c-à-d pas couvert par les accords syndicaux
| sur le temps de travail, le payement des heures supplémentaires,
| etc.


Normalement, la durée légale c'est 40h ici, et je « gagne »
8h de vacances à la de chaque mois (et 8h « d'heure maladie »). Il y a
deux ans, je voulais prendre des vacances qui éxcédaient de « 4 heures
» mon temps accumulé. Quelqu'un dans l'administration m'a expliqué que je
pouvais le faire uniquement si mon manager était d'accord qu'il me
faisait venir le samedi ou dimanche pour faire les heures
manquantes. Je suis resté sur le cul.

[ Évidemment, mon manager a expliqué que je ne met pas mon cerveau en
marche à 8h et le débranche à 17h :-) ]

-- Gaby
1 2 3 4 5