OVH Cloud OVH Cloud

Initialisation statique

15 réponses
Avatar
Guillaume LEMAÎTRE
Bonjour

J'aimerai savoir s'il existe des stratagèmes pour qu'une instance
constante d'une classe soit directement initialisée dans l'exécutable et
ne nécessite pas l'appel de constructeurs globaux.

Pour faire plus clair :

-->

class Biniou {
int a;
char b;

public :

const int get_a( void ) const { return a; };
const char get_b( void ) const { return b; };

int & set_a( void ) { return a; };
char & set_b( void ) { return b; };
};

// ligne invalide, mais qui veut bien dire ce qu'elle veut dire

const Biniou wesh = { 5, 'c' };

<--

La surcharge du constructeur amènerait à une initialisation dynamique et
non statique. Aujourd'hui j'en suis réduit à patcher la section 'data'
de l'exécutable pour réaliser mes initialisations, ce qui est efficace
mais peu propre...
Donc si l'un d'entre vous a une idée, elle est la bienvenue

PS: les initalisations peuvent être citiques dans le cas de programmes
temps-réel embarqués.

5 réponses

1 2
Avatar
kanze
Loïc Joly wrote in message
news:<bro627$l5h$...

Guillaume LEMAÎTRE wrote:

Loïc Joly wrote:

Guillaume LEMAÎTRE wrote:

La surcharge du constructeur amènerait à une initialisation
dynamique et non statique.


Je ne vois pas pourquoi. Peux-tu expliciter ce que tu veux dire ?


à supposer que l'on ait en plus :

-->

class Biniou {

// ...

Biniou( const int p_a, const char p_b ) : a( p_a ), b( p_b ) {};

// ...

};

// alors la ligne devient

const Biniou wesh = Biniou( 5, 'c' );

<--

cette déclaration provoquera avant l'entrée dans le main l'appel au
constructeur que l'on vient de surcharger qui se chargera
d'initialiser l'instance 'wesh'. C'est-à-dire du temps de perdu à
initialiser un objet dont on pouvait connaître par avance le contenu
!


Ah, je crois voir enfin ce que tu entendais par dynamique (j'avais
confondu avec le dynamique de à new ou malloc).

Si c'est bien le cas, je me pose alors quelques questions :

- Est-ce important ? Je n'ai pas l'impression que le temps
d'initialisation de quelques constantes soit en général un problème en
terme de performances.


Question temps, ça m'étonnerait. Question ordre d'initialisation, en
revanche...

- Un bon compilateur n'aurait-il pas le droit d'initialiser l'objet
wesh à la compilation ? Voire même de supprimer toute référence à wesh
du programme final et de remplacer tous les wesh.get_a() par 5 (puis
de continuer éventuellement à optimiser). Je n'ai rien vu dans la
norme qui semble empêcher ça (mais si je commence à savoir m'y
retrouver pour voir si quelquechose existe, je suis encore loin de la
connaître assez pour savoir qu'une chose n'existe pas), mais est-ce à
la portée des compilateurs actuels.


En général, il y a la règle de « comme si ». Mais attention ici. Si je
considère sa classe Biniou, avec le constructeur fourni par
l'utilisateur, et j'écris :

ClasseAvecCtor a ;
Biniou b( 42, 'a' ) ;

ClasseAvecCtor::ClasseAvecCtor()
{
std::cout << b.a << 'n' ;
}

J'aurais normalement 0 en sortie, et non 42 -- l'initialisation à zéro a
lieu avant les initialisations dynamique, et la norme garantit ici que a
serait initialisé avant b.

En particulier (3.6.2.2) :

An implementation is permitted to perform the initialization of an
object of namespace scope with static storage duration as a static
initialization even if such initialization is not required to be
done statically, provided that the dynamic version of the
initialization does not change the value of any other object of
namespace scope with static storage duration prior to its
initialization, and

- the static version of the initialization produces the same value
in the initialized object as would be produced by the dynamic
initialization if all objects not required to be initialized
statically were initialized dynamically.



Je connais le texte que tu cites, mais je n'ai jamais réussi à
comprendre exactement ce qu'il essaie à dire. Je ne saurais dire, par
exemple, s'il s'applique à mon exemple ci-dessus. Même l'exemple qui
suit n'est pas tout à fait clair. (Mais je crois que l'intention, c'est
bien que dans mon exemple ci-dessus, ce n'est pas spécifié si je sors 0
ou si je sors 42 -- le compilateur a le droit de remplacer
l'initialisation dynamique par l'initialisation statique dans ce
cas-ci, et cela, bien que le remplacement change l'ordre
d'initialisation, et donc la sémantique du programme. Mais je suis loins
d'en être sûr.)

- Si vraiment c'est important, si la classe Biniou est un aggregate,
une écriture comme celle que tu as écrit au début est possible et
t'assure d'une initialisation pendant la phase statique (8.5.1.14).


Il faut non seulement que Biniou soit un aggregate -- il faut qu'il ait
un constructeur trivial, et que toutes les expressions d'initialisation
soient des constantes, pour que l'initialisation soit statique.

When an aggregate with static storage duration is initialized with a
braceenclosed initializerlist, if all the member initializer
expressions are constant expressions, and the aggregate is a POD
type, the initialization shall be done during the static phase of
initialization (3.6.2); otherwise, it is unspecified whether the
initialization of members with constant expressions takes place
during the static phase or during the dynamic phase of
initialization.



Note bien la condition « and the aggregate is a POD type ».

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16




Avatar
Gabriel Dos Reis
writes:

[...]

| > | const Biniou wesh = Biniou( 5, 'c' );
|
| > | <--
|
| > | cette déclaration provoquera avant l'entrée dans le main l'appel au
| > | constructeur que l'on vient de surcharger qui se chargera
| > | d'initialiser l'instance 'wesh'. C'est-à-dire du temps de perdu à
| > | initialiser un objet dont on pouvait connaître par avance le contenu !
|
| > si tu arrives à mesurer le temps « perdu » à initialiser 'wesh' alors
| > c'est que ton programme ne fait rien d'intéressant ou il faut que tu
| > changes de compilateur.
|
| En ce qui concerne le temps, je suis d'accord avec toi, mais ça ne veut
| pas dire que l'initialisation statique est sans intérêt. Je peux penser
| à au moins deux cas où il a des avantages :

Je ne dis pas que l'initialisation statique n'a pas d'intérêt -- et je
n'ai rien prétendu de pareil.

-- Gaby
Avatar
Guillaume LEMAÎTRE
Loïc Joly wrote:
Guillaume LEMAÎTRE wrote:


En tout cas, merci d'avoir pris mon cas en considération !



Euh, tu n'a pas répondu sur ce que tu pensais de faire de ta classe un
aggregate, afin de pouvoir l'initialiser comme en C...



Ca n'est pas souhaitable car cela va à l'encontre du principe
d'interface des objets. Mais il doit être intéressant d'avoir une
structure du genre :

struct Biniou_Impl { /* POD */ };

class Biniou {
private :
Biniou_Impl c_toto;

// plus aucun champ, que des méthodes
// ...
};

Alors on peut réaliser statiquement l'initialisation d'une structure et
l'utiliser via un opérateur de placement. Le problème d'une telle
architecture est que l'on ne peut pas avoir des classes de classes, ce
qui est assez limitant, finalement.


Avatar
Guillaume LEMAÎTRE
wrote:
Guillaume LEMAÎTRE wrote in message
news:<brne5n$ffh$...


J'aimerai savoir s'il existe des stratagèmes pour qu'une instance
constante d'une classe soit directement initialisée dans l'exécutable
et ne nécessite pas l'appel de constructeurs globaux.



Pour faire plus clair :



-->



class Biniou {
int a;
char b;



public :



const int get_a( void ) const { return a; };
const char get_b( void ) const { return b; };



int & set_a( void ) { return a; };
char & set_b( void ) { return b; };
};



// ligne invalide, mais qui veut bien dire ce qu'elle veut dire



const Biniou wesh = { 5, 'c' };



<--



Pourquoi c'est invalide ? Elle a l'air parfaitement valide pour moi.
J'utilise parfois quelque chose du genre :

template< typename T1, typename T2 >
struct pod_pair
{
T1 first ;
T2 second ;
operator std::pair< T1, T2 >()
{
return std::pair< T1, T2 >( first, second ) ;
}
} ;

exprès pour me permettre d'écrire par exemple :

pod_pair< char const*, int > const
initTable[] > {
{ "toto", 42 },
{ "titi", 34 },
// ...
} ;
std::map< std::string, int >
map( begin( initTable ), end( initTable ) ) ;



La différence à mes yeux est que ta structure pod_pair n'a pas de notion
d'interface, c'est-à-dire que tout est visible (et particulièrement les
champs).
Dans ma classe Biniou, mes champs sont privés et je fournis des
accesseurs sur ceux-ci. J'ai qualifié ma ligne d'initialisation invalide
car il me semblerait choquant de pouvoir initialiser une classe à partir
d'un aggrégat alors que je ne suis pas sensé connaître son
implémentation mais seulement son interface. Et d'ailleurs, cela choque
gcc également (depuis de très vieilles versions).
L'exemple que tu me fournis fonctionne, et cela ne me choque guère car
tout est visible.

La plupart du temps, le but n'est pas tant d'éviter l'initialisation
dynamique, mais de facilité l'écriture, mais si les deux éléments du
pod_pair sont bien POD eux-même, l'initialisation en est bien statique.



tout à fait.


Aujourd'hui j'en suis réduit à patcher la section 'data' de
l'exécutable pour réaliser mes initialisations, ce qui est efficace
mais peu propre... Donc si l'un d'entre vous a une idée, elle est la
bienvenue



Pour l'instant, je ne vois pas où est le problème. Une classe serait
initialisée statiquement si son constructeur est trivial, et que les
initialisations sont tous des expressions constantes. Le constructeur
est trivial s'il n'est pas déclaré par le programmeur, et

- la classe n'a pas de fonctions virtuelles ni de classes de base
virtuelle,
- toutes les classes de bases ont des constructeurs triviaux, et
- tous les membres données non-statiques qui ont un type de classe ont
des constructeurs triviaux.

(Note bien que dans mon pod_pair, si j'instancie avec le premier type
std::string à la place de char const*, l'initialisation ne serait plus
statique, mais dynamique.)



tout à fait d'accord. Néanmoins je tiens à replacer le contexte : nous
sommes dans un contexte industriel. Vu l'extrait cité par Loïc, un bon
compilateur pourrait mener à bien toutes ces optimisations en analysant
l'ensemble des types dépendants et en générant donc ma constante en
section 'read-only data'. Mais tous les compilateurs ne sont pas de bons
compilateurs, loin s'en faut (malheureusement).


PS: les initalisations peuvent être citiques dans le cas de programmes
temps-réel embarqués.



Surtout si ces programmes sont en PROM, n'est-ce pas ?



Exactement. Aujourd'hui mes constantes sont initialisées dynamiquement,
et sont donc de fait dupliquées entre la ROM et la RAM pour être
initialisées, ce qui est ridicule !
De plus, le compilateur utilisé génère plus de code pour initialiser une
constante que de données constantes servant à cette initialisation. Par
exemple, une constante de 3ko théoriquement finit par occuper 5ko de
code et 3ko de données ! Et il paraît a priori inutile de devoir
exécuter ces 5ko de code puisque l'état de ma constante *devrait* être
connu statiquement (et malheureusement, je suis loin de n'avoir qu'une
seule constante à gérer...)

En général, même dans le cas des programmes sur ordinateur général (PC,
Sun, etc.), l'initialisation statique a l'avantage de ne pas introduire
des dépendences dans l'ordre d'initialisation -- quoiqu'il arrive, je
sais que ma variable « initTable » ci-dessus serait initialisée avant
son utilisation.



La gestion de l'ordre des ctors est également problématique sur cette
application, tu fais bien de le souligner :)


Avatar
kanze
Guillaume LEMAÎTRE wrote in message
news:<brq52k$g93$...
wrote:
Guillaume LEMAÎTRE wrote in message
news:<brne5n$ffh$...

Pourquoi c'est invalide ? Elle a l'air parfaitement valide pour moi.
J'utilise parfois quelque chose du genre :

template< typename T1, typename T2 >
struct pod_pair
{
T1 first ;
T2 second ;
operator std::pair< T1, T2 >()
{
return std::pair< T1, T2 >( first, second ) ;
}
} ;

exprès pour me permettre d'écrire par exemple :

pod_pair< char const*, int > const
initTable[] > > {
{ "toto", 42 },
{ "titi", 34 },
// ...
} ;
std::map< std::string, int >
map( begin( initTable ), end( initTable ) ) ;


La différence à mes yeux est que ta structure pod_pair n'a pas de
notion d'interface, c'est-à-dire que tout est visible (et
particulièrement les champs).

Dans ma classe Biniou, mes champs sont privés et je fournis des
accesseurs sur ceux-ci. J'ai qualifié ma ligne d'initialisation
invalide car il me semblerait choquant de pouvoir initialiser une
classe à partir d'un aggrégat alors que je ne suis pas sensé connaître
son implémentation mais seulement son interface. Et d'ailleurs, cela
choque gcc également (depuis de très vieilles versions). L'exemple que
tu me fournis fonctionne, et cela ne me choque guère car tout est
visible.


En effet, l'initialisation comme aggregate ne vaut que si tous les
champs non-statiques sont publiques. Et qu'il n'y a pas de
constructeurs.

Je ne sais pas si ça serait utile dans ton cas, mais note bien que si
mon aggregate est un aggregate, std::pair ne l'est pas, mais qu'on peut
se servir de mon aggregate partout où on a besoin d'un std::pair. Le
même principe peut s'appliquer à des choses plus compliquées.

La plupart du temps, le but n'est pas tant d'éviter l'initialisation
dynamique, mais de facilité l'écriture, mais si les deux éléments du
pod_pair sont bien POD eux-même, l'initialisation en est bien
statique.


tout à fait.


Mais il faut savoir qu'on veut. Si on veut initialiser les champs
(initialisation d'aggregate), il faut qu'ils soient visibles.

Aujourd'hui j'en suis réduit à patcher la section 'data' de
l'exécutable pour réaliser mes initialisations, ce qui est efficace
mais peu propre... Donc si l'un d'entre vous a une idée, elle est la
bienvenue


Pour l'instant, je ne vois pas où est le problème. Une classe serait
initialisée statiquement si son constructeur est trivial, et que les
initialisations sont tous des expressions constantes. Le
constructeur est trivial s'il n'est pas déclaré par le programmeur,
et

- la classe n'a pas de fonctions virtuelles ni de classes de base
virtuelle,
- toutes les classes de bases ont des constructeurs triviaux, et
- tous les membres données non-statiques qui ont un type de classe ont
des constructeurs triviaux.

(Note bien que dans mon pod_pair, si j'instancie avec le premier
type std::string à la place de char const*, l'initialisation ne
serait plus statique, mais dynamique.)


tout à fait d'accord. Néanmoins je tiens à replacer le contexte : nous
sommes dans un contexte industriel. Vu l'extrait cité par Loïc, un bon
compilateur pourrait mener à bien toutes ces optimisations en
analysant l'ensemble des types dépendants et en générant donc ma
constante en section 'read-only data'. Mais tous les compilateurs ne
sont pas de bons compilateurs, loin s'en faut (malheureusement).


À qui tu le dis ? Et il faut vivre avec ce qu'on a -- on n'est pas payé
pour dire du mal des compilateurs, mais pour faire marcher une
application avec les outils qu'on a.

PS: les initalisations peuvent être citiques dans le cas de
programmes temps-réel embarqués.


Surtout si ces programmes sont en PROM, n'est-ce pas ?


Exactement. Aujourd'hui mes constantes sont initialisées
dynamiquement, et sont donc de fait dupliquées entre la ROM et la RAM
pour être initialisées, ce qui est ridicule !

De plus, le compilateur utilisé génère plus de code pour initialiser
une constante que de données constantes servant à cette
initialisation. Par exemple, une constante de 3ko théoriquement finit
par occuper 5ko de code et 3ko de données ! Et il paraît a priori
inutile de devoir exécuter ces 5ko de code puisque l'état de ma
constante *devrait* être connu statiquement (et malheureusement, je
suis loin de n'avoir qu'une seule constante à gérer...)


Dans ce cas-là, à mon avis, il faut savoir faire des compromis. Et se
servir des champs de données publics.

En général, même dans le cas des programmes sur ordinateur général
(PC, Sun, etc.), l'initialisation statique a l'avantage de ne pas
introduire des dépendences dans l'ordre d'initialisation --
quoiqu'il arrive, je sais que ma variable « initTable » ci-dessus
serait initialisée avant son utilisation.


La gestion de l'ordre des ctors est également problématique sur cette
application, tu fais bien de le souligner :)


C'est souvent problèmatique:-).

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16



1 2