Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Singleton, const et non-const

9 réponses
Avatar
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

9 réponses

Avatar
James Kanze
On Jul 22, 10:38 am, 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 :

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

Avatar
Michael
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...
Avatar
Michel Decima
On Jul 22, 10:38 am, 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 :



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


Avatar
James Kanze
On Jul 30, 5:03 pm, Michel Decima wrote:

On Jul 22, 10:38 am, 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 :



[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



Avatar
Jean-Marc Desperrier
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é.

Avatar
Michel Decima
On Jul 30, 5:03 pm, Michel Decima wrote:

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.


Avatar
Michel Decima
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.


Avatar
Jean-Marc Desperrier
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.


Avatar
Michel Decima
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...