Singleton, const et non-const

Le
Serge Paccalin
Après bien des tâtonnements, j'ai fini par me ranger à la même méthode
que James Kanze pour faire mes singletons, à savoir :

--
class Machin
{
public:
static Machin &instance();



private:
Machin();
~Machin();
};

static Machin &Machin::instance()
{
static Machin LeSeul;
return LeSeul;
}
--

et je me demandais si ça ne valait pas le coup de faire une deuxième
fonction, const celle-là, pour accéder au singleton en s'interdisant de
le modifier, pour détecter d'éventuelles erreurs. Quelque chose comme :

--
static const Machin &Machin::const_instance()
{
return Machin::instance();
}
--

Éventuellement, en renommant instance() la version const, et
mutable_instance() la version non-const (pour inciter le programmeur à
utiliser la version const le plus souvent possible).

Un avis ?

--
___________
_/ _ _`_`_`_) Serge PACCALIN -- sp ad mailclub.net
_L_) Il faut donc que les hommes commencent
-'(__) par n'être pas fanatiques pour mériter
_/___(_) la tolérance. -- Voltaire, 1763
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
James Kanze
Le #309159
On Jul 22, 10:38 am, Serge Paccalin
Après bien des tâtonnements, j'ai fini par me ranger à la même m éthode
que James Kanze pour faire mes singletons, à savoir :

-----------------------------------------------------
class Machin
{
public:
static Machin &instance();

...

private:
Machin();
~Machin();
};

static Machin &Machin::instance()
{
static Machin LeSeul;
return LeSeul;}

-----------------------------------------------------


Sauf que ce n'est pas la méthode que j'utilise la plupart du
temps:-). En fait, j'ai fini dépuis par en faire une classe
générique :

enum DestructionPolicy
{
neverDestruct,
destructOnExit
} ;

template< typename UserClass, DestructionPolicy dtorPolicy =
neverDestruct >
class Singleton
{
public:
static UserClass& instance() ;

private:
static UserClass* ourInstance ;

template< DestructionPolicy discrimPolicy >
class Discrim {} ;
static UserClass* createInstance( Discrim< neverDestruct
) ;
static UserClass* createInstance( Discrim< destructOnExit

) ;
} ;



template< typename UserClass, DestructionPolicy dtorPolicy >
UserClass* Singleton< UserClass, dtorPolicy >::
ourInstance
= &Singleton< UserClass, dtorPolicy >::instance() ;

template< typename UserClass, DestructionPolicy dtorPolicy >
UserClass&
Singleton< UserClass, dtorPolicy >::instance()
{
if ( ourInstance == NULL ) {
ourInstance = createInstance( Discrim< dtorPolicy >() ) ;
}
return *ourInstance ;
}

template< typename UserClass, DestructionPolicy dtorPolicy >
UserClass*
Singleton< UserClass, dtorPolicy >::createInstance(
Discrim< neverDestruct > )
{
return new UserClass ;
}

template< typename UserClass, DestructionPolicy dtorPolicy >
UserClass*
Singleton< UserClass, dtorPolicy >::createInstance(
Discrim< destructOnExit > )
{
static UserClass theOneAndOnly ;
return &theOneAndOnly ;
}

Ce qui donne l'utilisateur le choix, mais le défaut, c'est
l'utilisation d'une instance allouée dynamiquement (afin que le
destructeur n'en soit jamais appelé), ce qui correspond à mon
implémentation habituelle d'avant la classe générique.

Pour ceux qui ne connaissent pas l'idiome, la classe réelement
singleton dérive de ce template, instantié sur elle-même,
c-à-d :
class MySingleton : public Singleton< MySingleton >
{
// ...
} ;
. Pendant longtemps, j'ai considéré que la logique de singleton
était tellement simple qu'il ne valait pas de template. Mais
quand on considère d'une part la nécessité d'en assurer la
construction avant le démarrage des threads (pour ne pas avoir à
se payer le prix d'un lock dans « instance() »), et l'avantage
qu'il se déclare explicitement que le destructeur n'est jamais
appelé (pour éviter des problèmes de l'ordre de destruction),
j'ai changé d'avis.

et je me demandais si ça ne valait pas le coup de faire une deuxième
fonction, const celle-là, pour accéder au singleton en s'interdisant de
le modifier, pour détecter d'éventuelles erreurs. Quelque chose comme :

-----------------------------------------------------
static const Machin &Machin::const_instance()
{
return Machin::instance();}

-----------------------------------------------------

Éventuellement, en renommant instance() la version const, et
mutable_instance() la version non-const (pour inciter le
programmeur à utiliser la version const le plus souvent
possible).


Si ça te dit, pourquoi pas ? J'ai bien des singletons où je
renvoie une référence const, parce que l'objet est immutable.
Mais en général, je n'ai pas trouvé que la correction de const
apportait beaucoup dans ce cas-ci. L'importance de const, c'est
quand c'est moi qui t'appelle, et que tu promets de ne rien
modifier. Ici, qui promet quoi à qui ?

--
James Kanze (Gabi Software) email:
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

Michael
Le #309867
Il existe une version du singleton, utilisant aussi beaucoup les policies,
qui a été proposée par Alexandrescu, qui propose quelques options
supplémentaires encore...
Michel Decima
Le #310322
On Jul 22, 10:38 am, Serge Paccalin
Après bien des tâtonnements, j'ai fini par me ranger à la même méthode
que James Kanze pour faire mes singletons, à savoir :



[snip]

Sauf que ce n'est pas la méthode que j'utilise la plupart du
temps:-). En fait, j'ai fini dépuis par en faire une classe
générique :
[snip]


Dans la classe Machin de Serge, le constructeur est prive, donc
tu ne peux pas declarer une autre instance de Machin que celle du
singleton.
Dans ta classe Singleton<>, que j'aime assez par ailleurs, je ne vois
pas comment on obtient le meme comportement, a part declarer le
constructeur de la classe derivee en privé, mais il faut alors un
friend sinon createInstance() ne compile plus:

class MySingleton : public Singleton< MySingleton >
{
private:
friend class Singleton< MySingleton >;
MySingleton();
~MySingleton();
};

Je n'ai rien contre le ctor/dtor en privé, ca indique clairement
ce que ca veut dire, mais je trouve que le friend est un peu redondant.
Est-ce qu'il y a un moyen de s'en passer ? Est-ce que j'ai raté
quelque-chose ?

MD.


James Kanze
Le #310318
On Jul 30, 5:03 pm, Michel Decima
On Jul 22, 10:38 am, Serge Paccalin
Après bien des tâtonnements, j'ai fini par me ranger à la même méthode
que James Kanze pour faire mes singletons, à savoir :



[snip]

Sauf que ce n'est pas la méthode que j'utilise la plupart du
temps:-). En fait, j'ai fini dépuis par en faire une classe
générique :


[snip]

Dans la classe Machin de Serge, le constructeur est prive, donc
tu ne peux pas declarer une autre instance de Machin que celle du
singleton.
Dans ta classe Singleton<>, que j'aime assez par ailleurs, je ne vois
pas comment on obtient le meme comportement, a part declarer le
constructeur de la classe derivee en privé, mais il faut alors un
friend sinon createInstance() ne compile plus:

class MySingleton : public Singleton< MySingleton >
{
private:
friend class Singleton< MySingleton >;
MySingleton();
~MySingleton();
};


Tout à fait.

Je n'ai rien contre le ctor/dtor en privé, ca indique clairement
ce que ca veut dire, mais je trouve que le friend est un peu redondant.
Est-ce qu'il y a un moyen de s'en passer ? Est-ce que j'ai raté
quelque-chose ?


Je ne vois pas comment s'en passer, au moins de laisser le
constructeur public (ce qui permettrait d'autres
instantiations -- parfois, c'est acceptable, mais c'est un peu
contre l'idée de singleton).

--
James Kanze (GABI Software) email:
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 Desperrier
Le #310317
Serge Paccalin wrote:
Après bien des tâtonnements, j'ai fini par me ranger à la même méthode
que James Kanze pour faire mes singletons, à savoir :


Pour java, mais bon le raisonnement s'applique totalement au C++, Google
diffuse un outils de détection automatique des Singleton, dans
l'objectif avoué de leur élimination systématique :
http://code.google.com/p/google-singleton-detector/

L'outils signale aussi l'arbre de dépendance des classe dépendant de la
classe Singleton, et aussi les autres usage de données statiques,
mingleton et fingleton, les classes contenant un membre ou bien un champ
stockant un état statique.

Bref pour Google la guerre anti-singleton, et tous dérivés, est ouverte,
et l'usage de l'artillerie lourde est encouragé.

Michel Decima
Le #310295
On Jul 30, 5:03 pm, Michel Decima
class MySingleton : public Singleton< MySingleton >
{
private:
friend class Singleton< MySingleton >;
MySingleton();
~MySingleton();
};


Tout à fait.

Je n'ai rien contre le ctor/dtor en privé, ca indique clairement
ce que ca veut dire, mais je trouve que le friend est un peu redondant.
Est-ce qu'il y a un moyen de s'en passer ? Est-ce que j'ai raté
quelque-chose ?


Je ne vois pas comment s'en passer, au moins de laisser le
constructeur public (ce qui permettrait d'autres
instantiations -- parfois, c'est acceptable, mais c'est un peu
contre l'idée de singleton).


Ok, je vois l'idee. Si on veut une instance unique, on peut le faire
(et la doc d'utilisation doit y inciter fortement) avec les ctor/dtor
prives et le friend, mais si on veut un autre comportement, on a quand
meme la liberté de le faire aussi.


Michel Decima
Le #310294
Serge Paccalin wrote:
Après bien des tâtonnements, j'ai fini par me ranger à la même méthode
que James Kanze pour faire mes singletons, à savoir :


Pour java, mais bon le raisonnement s'applique totalement au C++, Google
diffuse un outils de détection automatique des Singleton, dans
l'objectif avoué de leur élimination systématique :
http://code.google.com/p/google-singleton-detector/

L'outils signale aussi l'arbre de dépendance des classe dépendant de la
classe Singleton, et aussi les autres usage de données statiques,
mingleton et fingleton, les classes contenant un membre ou bien un champ
stockant un état statique.


Par curiosité, c'est quoi les {m|f|h}ingleton ? J'ai suivi le lien,
cherché un peu autour, mais rien trouvé...

Bref pour Google la guerre anti-singleton, et tous dérivés, est ouverte,
et l'usage de l'artillerie lourde est encouragé.


Est-ce vraiment utile ? Je me pose la question parce que j'ai deja vu du
code ou quasiment chaque classe etait un singleton (sans aucune raison
valable), et alors il n'y a pas besoin d'un outil automatique pour les
detecter. D'un autre coté, un Singleton utilise a bon escient (et bien
sur avec parcimonie), ca se remplace difficilement.


Jean-Marc Desperrier
Le #310293
Michel Decima wrote:
L'outils signale aussi l'arbre de dépendance des classe dépendant de
la classe Singleton, et aussi les autres usage de données statiques,
mingleton et fingleton, les classes contenant un membre ou bien un
champ stockant un état statique.


Par curiosité, c'est quoi les {m|f|h}ingleton ? J'ai suivi le lien,
cherché un peu autour, mais rien trouvé...


C'est décrit ici
http://code.google.com/p/google-singleton-detector/wiki/Usage et c'est
ce que je résumais, member singleton => mingleton, field singleton =>
fingleton, helper singleton => hingleton, la définition est un peu plus
floue pour le dernier.

Bref pour Google la guerre anti-singleton, et tous dérivés, est
ouverte, et l'usage de l'artillerie lourde est encouragé.


Est-ce vraiment utile ? [...]
D'un autre coté, un Singleton utilise a bon escient (et bien
sur avec parcimonie), ca se remplace difficilement.


Les singleton évitent de passer des paramètres, mais introduisent un
état statique qui finit un jour ou un autre par être génant, en
particulier qui pose des problèmes par rapport à une programmation
parallèle (ça doit probablement être absolument essentiel pour Google)
et qui gène pour tester.


Michel Decima
Le #310291
Michel Decima wrote:

Par curiosité, c'est quoi les {m|f|h}ingleton ? J'ai suivi le lien,
cherché un peu autour, mais rien trouvé...


C'est décrit ici
http://code.google.com/p/google-singleton-detector/wiki/Usage et c'est
ce que je résumais, member singleton => mingleton, field singleton =>
fingleton, helper singleton => hingleton, la définition est un peu plus
floue pour le dernier.


Oups, ma faute, j'avais un peu lu la page en travers... merci tout de meme.

Les singleton évitent de passer des paramètres, mais introduisent un
état statique qui finit un jour ou un autre par être génant, en
particulier qui pose des problèmes par rapport à une programmation
parallèle (ça doit probablement être absolument essentiel pour Google)
et qui gène pour tester.


Ok, j'admet que ca peut etre genant dans certains cas, mais je crois
que je ne les ai pas (encore) rencontrés...


Publicité
Poster une réponse
Anonyme