C++ singleton classe de modèle
Dans un projet récent, j'ai dû créer une classe Singleton et après beaucoup de creuser autour sur Google j'ai trouvé ce modèle de définition de la classe. L'idée est de dériver de cette classe de modèle et de faire de la dérivée de la classe de constructeur protected /private. Il semble bien fonctionner mais je ne l'ai utilisé avec une seule classe dans un projet, j'ai donc été en espérant que certains d'entre vous pourraient rappeler si j'ai fait des erreurs dans la mise en œuvre. Ici, il est:
/**
* @brief
* Singleton design pattern implementation using a dynamically allocated singleton instance.
*
* The SingletonDynamic class is intended for use as a base for classes implementing the Singleton
* design pattern and require lazy initialization of the singleton object. The default
* implementation is not thread-safe, however, the derived classes can make it so by reinitializing
* the function pointers SingletonDynamic<T>::pfnLockMutex, SingletonDynamic<T>::pfnUnlockMutex
* and SingletonDynamic<T>::pfnMemoryBarrier. The member function pointers are initialized by
* default to point to placeholder functions that do not perform any function. The derived class
* must provide alternate implementations for SingletonDynamic<T>::lock_mutex(),
* SingletonDynamic<T>::unlock_mutex() and SingletonDynamic<T>::memory_barrier() respectively
* and reinitialize the respective function pointer members to these alternate implementations.
*
* @tparam T
* The type name of the derived (singleton) class
*
* @note The derived class must have a no-throw default constructor and a no-throw destructor.
* @note The derived class must list this class as a friend, since, by necessity, the derived class'
* constructors must be protected /private.
*/
template< typename T >
class SingletonDynamic
{
public:
/**
* Factory function for vending mutable references to the sole instance of the singleton object.
*
* @return A mutable reference to the one and only instance of the singleton object.
*/
static T &instance()
{
return *SingletonDynamic< T >::get_instance();
}
/**
* Factory function for vending constant references to the sole instance of the singleton object.
*
* @return A constant reference to the one and only instance of the singleton object.
*/
static const T &const_instance()
{
return *SingletonDynamic< T >::get_instance();
}
protected:
/** Default constructor */
SingletonDynamic() {}
/** Destructor */
virtual ~SingletonDynamic()
{
delete SingletonDynamic< T >::pInstance_;
}
/** Defines an alias for a function pointer type for executing functions related to thread-safety */
typedef void(*coherence_callback_type)();
/**
* Pointer to a function that will lock a mutex denying access to threads other that the current
*
* @note The function must have the signature void foo()
* @note The derived class must never set this variable to NULL, doing so will cause a crash. The
* default value must be left unchanged if this functionality is not desired.
*/
static coherence_callback_type pfnLockMutex;
/**
* Pointer to a function that will unlock a mutex allowing access to other threads
*
* @note The function must have the signature void foo()
* @note The derived class must never set this variable to NULL, doing so will cause a crash. The
* default value must be left unchanged if this functionality is not desired.
*/
static coherence_callback_type pfnUnlockMutex;
/**
* Pointer to a function that executes a memory barrier instruction that prevents the compiler
* from reordering reads and writes across this boundary.
*
* @note The function must have the signature void foo()
* @note The derived class must never set this variable to NULL, doing so will cause a crash. The
* default value must be left unchanged if this functionality is not desired.
*/
static coherence_callback_type pfnMemoryBarrier;
private:
/** The sole instance of the singleton object */
static T *pInstance_;
/** Flag indicating whether the singleton object has been created */
static volatile bool flag_;
/** Private copy constructor to prevent copy construction */
SingletonDynamic( SingletonDynamic const & );
/** Private operator to prevent assignment */
SingletonDynamic &operator=( SingletonDynamic const & );
/**
* Fetches a pointer to the singleton object, after creating it if necessary
*
* @return A pointer to the one and only instance of the singleton object.
*/
static T *get_instance()
{
if( SingletonDynamic< T >::flag_ == false ) {
/* acquire lock */
(*SingletonDynamic< T >::pfnLockMutex)();
if( SingletonDynamic< T >::pInstance_ == NULL ) {
pInstance_ = new T();
}
/* release lock */
(*SingletonDynamic< T >::pfnUnlockMutex)();
/* enforce all prior I/O to be completed */
(*SingletonDynamic< T >::pfnMemoryBarrier)();
SingletonDynamic< T >::flag_ = true;
return SingletonDynamic< T >::pInstance_;
} else {
/* enforce all prior I/O to be completed */
(*SingletonDynamic< T >::pfnMemoryBarrier)();
return SingletonDynamic< T >::pInstance_;
}
}
/**
* Placeholder function for locking a mutex, thereby preventing access to other threads. This
* default implementation does not perform any function, the derived class must provide an
* implementation if this functionality is desired.
*/
inline static void lock_mutex()
{
/* default implementation does nothing */
return;
}
/**
* Placeholder function for unlocking a mutex, thereby allowing access to other threads. This
* default implementation does not perform any function, the derived class must provide an
* implementation if this functionality is desired.
*/
inline static void unlock_mutex()
{
/* default implementation does nothing */
return;
}
/**
* Placeholder function for executing a memory barrier instruction, thereby preventing the
* compiler from reordering read and writes across this boundary. This default implementation does
* not perform any function, the derived class must provide an implementation if this
* functionality is desired.
*/
inline static void memory_barrier()
{
/* default implementation does nothing */
return;
}
};
/* Initialize the singleton instance pointer */
template< typename T >
T *SingletonDynamic<T>::pInstance_ = NULL;
/* Initialize the singleton flag */
template< typename T >
volatile bool SingletonDynamic<T>::flag_ = false;
/* Initialize the function pointer that locks the mutex */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnLockMutex
= &SingletonDynamic<T>::lock_mutex;
/* Initialize the function pointer that unlocks the mutex */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnUnlockMutex
= &SingletonDynamic<T>::unlock_mutex;
/* Initialize the function pointer that executes the memory barrier instruction */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnMemoryBarrier
= &SingletonDynamic<T>::memory_barrier;
Je suis particulièrement préoccupé par le membre statique initialisations dans le fichier d'en-tête et si cela va causer de multiples erreurs de définition lorsque le fichier d'en-tête de la classe est dérivée de la SingleDynamic est inclus dans plusieurs fichiers. J'ai déjà essayé ça et ça semble marcher, mais je ne peux pas comprendre pourquoi sa fonctionne :).
Merci d'avance,
Ashish.
EDIT: Modifié la mise en œuvre d'une politique de conception comme le suggère la solution retenue.
/**
* This is the default ConcurrencyPolicy implementation for the SingletonDynamic class. This
* implementation does not provide thread-safety and is merely a placeholder. Classes deriving from
* SingletonDynamic must provide alternate ConcurrencyPolicy implementations if thread-safety is
* desired.
*/
struct DefaultSingletonConcurrencyPolicy
{
/**
* Placeholder function for locking a mutex, thereby preventing access to other threads. This
* default implementation does not perform any function, the derived class must provide an
* alternate implementation if this functionality is desired.
*/
static void lock_mutex()
{
/* default implementation does nothing */
return;
}
/**
* Placeholder function for unlocking a mutex, thereby allowing access to other threads. This
* default implementation does not perform any function, the derived class must provide an
* alternate implementation if this functionality is desired.
*/
static void unlock_mutex()
{
/* default implementation does nothing */
return;
}
/**
* Placeholder function for executing a memory barrier instruction, thereby preventing the
* compiler from reordering read and writes across this boundary. This default implementation does
* not perform any function, the derived class must provide an alternate implementation if this
* functionality is desired.
*/
static void memory_barrier()
{
/* default implementation does nothing */
return;
}
};
/**
* @brief
* Singleton design pattern implementation using a dynamically allocated singleton instance.
*
* The SingletonDynamic class is intended for use as a base for classes implementing the Singleton
* design pattern and that dynamic allocation of the singleton object. The default implementation
* is not thread-safe; however, the class uses a policy-based design pattern that allows the derived
* classes to achieve threaad-safety by providing an alternate implementation of the
* ConcurrencyPolicy.
*
* @tparam T
* The type name of the derived (singleton) class
* @tparam ConcurrencyPolicy
* The policy implementation for providing thread-safety
*
* @note The derived class must have a no-throw default constructor and a no-throw destructor.
* @note The derived class must list this class as a friend, since, by necessity, the derived class'
* constructors must be protected /private.
*/
template< typename T, typename ConcurrencyPolicy = DefaultSingletonConcurrencyPolicy >
class SingletonDynamic : public ConcurrencyPolicy
{
public:
/**
* Factory function for vending mutable references to the sole instance of the singleton object.
*
* @return A mutable reference to the one and only instance of the singleton object.
*/
static T &instance()
{
return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance();
}
/**
* Factory function for vending constant references to the sole instance of the singleton object.
*
* @return A constant reference to the one and only instance of the singleton object.
*/
static const T &const_instance()
{
return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance();
}
protected:
/** Default constructor */
SingletonDynamic() {}
/** Destructor */
virtual ~SingletonDynamic()
{
delete SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
}
private:
/** The sole instance of the singleton object */
static T *pInstance_;
/** Flag indicating whether the singleton object has been created */
static volatile bool flag_;
/** Private copy constructor to prevent copy construction */
SingletonDynamic( SingletonDynamic const & );
/** Private operator to prevent assignment */
SingletonDynamic &operator=( SingletonDynamic const & );
/**
* Fetches a pointer to the singleton object, after creating it if necessary
*
* @return A pointer to the one and only instance of the singleton object.
*/
static T *get_instance()
{
if( SingletonDynamic< T, ConcurrencyPolicy >::flag_ == false ) {
/* acquire lock */
ConcurrencyPolicy::lock_mutex();
/* create the singleton object if this is the first time */
if( SingletonDynamic< T, ConcurrencyPolicy >::pInstance_ == NULL ) {
pInstance_ = new T();
}
/* release lock */
ConcurrencyPolicy::unlock_mutex();
/* enforce all prior I/O to be completed */
ConcurrencyPolicy::memory_barrier();
/* set flag to indicate singleton has been created */
SingletonDynamic< T, ConcurrencyPolicy >::flag_ = true;
return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
} else {
/* enforce all prior I/O to be completed */
ConcurrencyPolicy::memory_barrier();
return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
}
}
};
/* Initialize the singleton instance pointer */
template< typename T, typename ConcurrencyPolicy >
T *SingletonDynamic< T , ConcurrencyPolicy >::pInstance_ = NULL;
/* Initialize the singleton flag */
template< typename T, typename ConcurrencyPolicy >
volatile bool SingletonDynamic< T , ConcurrencyPolicy >::flag_ = false;
Je suis d'accord avec GMan à ne pas utiliser singeltons. Je suis en désaccord qu'ils sont les mêmes que globals (initialisation). Et je déteste utiliser des pointeurs comme il y a la représentation que ce ne sont pas supprimés automatiquement (utilisation d'une fonction statique de la variable à l'intérieur de get_instance() (garder les verrous) de cette façon, le singleton sera supprimé correctement). PS. Vous avez besoin de déplacer
flag_ = true;
dans les serrures, sinon vous risquez d'avoir plusieurs threads création de l'instance.Eh bien, vous pouvez voler le creative solutions de création de singletons utiliser pour faire un beau mondial de l'utilitaire de bibliothèque. C'est surtout l'restreint instance de la merde c'est abrutissant et inutile. Je suis envisagent de soumettre une bibliothèque globale de coup de pouce, car il en manque un.
Il n'est pas question que
flag_ = true;
est à l'extérieur de l'exclusion mutuelle verrous; un seul thread peut aller au-delà de la (*SingletonDynamic< T >::pfnLockMutex)();
et ce thread va modifier la pInstance_
variable donc ce n'est pas NULL. Cela permettra d'éviter un thread en attente à la serrure de la création d'une nouvelle instance.Vous êtes correct. Oups.
OriginalL'auteur Praetorian | 2010-08-09
Vous devez vous connecter pour publier un commentaire.
L'exactitude de la concurrence liées code ici est difficile à évaluer. La mise en œuvre est d'essayer d'être un peu trop intelligent, à mon avis.
Otoh, que, tous les simultanéité connexes code de coeur a talons derrière elle que de ne rien faire. Si c'est utilisé dans un non filetée de l'environnement, je pense que ça devrait être bon.
Mais, je pense aussi que votre souci est le bien-fondé. Les définitions de la statique des membres semblent comme ils contreviendrait à la une définition de la règle.
Personnellement, je pense que ce modèle doit être ré-écrit pour avoir la simultanéité des trucs comme un argument stratégique pour le modèle lui-même, et à exiger que les classes dérivées de déclarer leurs propres versions de
pInstance
appropriées .fichier cpp.Quelqu'un d'autre a suggéré en s'appuyant sur compilateur comportement spécifique en ce qui concerne l'initialisation des variables locales statiques. Je ne pense pas que c'est un horrible suggestion, mais il pourrait être agréable d'avoir une option lorsque vous ne pouvez pas compter sur le compilateur de faire la bonne chose.
OriginalL'auteur Omnifarious
Membres statiques de classes template doit être initialisé dans le fichier d'en-tête et les récentes compilateurs C++ et leurs agents doivent gérer cela correctement.
Mais vous avez raison, certains très vieux compilateur avez des problèmes avec cela.
Dans ces cas, c'est une solution de contournement pour initialiser les membres statiques exactement une fois dans un arbitraire unité de compilation pour chaque type singleton modèle est utilisé pour.
La gcc de la Documentation a même des détails à ce sujet: http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html.
Je me souviens d'un projet intégré (pas longtemps), où un vieux compilateur était encore en usage, et que l'on silencieusement créé plusieurs instances d'un modèle statique de membres.
Évidemment une très mauvaise idée quand il viennent à stocker un Singleton....
Pire encore, le seul Singleton dans l'utilisation de la bibliothèque (3e partie-cadre) a été la Configuration de l'objet qui, généralement, a été initialisé de la même manière, de sorte que le bug ne se produisait lorsque la configuration a été modifiée lors de l'exécution.
Il a fallu plusieurs jours pour suivre le bug vers le bas, jusqu'à ce que nous avons enfin vu dans le démontage que le "même" membre est accessible à différentes régions de la mémoire.
OriginalL'auteur IanH
N'a pas besoin d'être complexe.
Simple C++ enregistreur en utilisant le pattern singleton
le lien ci-dessus utilise juste un statique objet singleton. Pourquoi est-ce que le compilateur spécifique? N'est-il pas vrai pour tous les compilateurs que tous les objets statiques sont construits avant
main()
exécute? De sorte que la mise en œuvre doit être thread-safe tant que la totalité de votre appel threads en cours d'exécution aprèsmain()
commence à s'exécuter.Pas de. fonction statique objets sont créés uniquement lors de la première utilisation (permettant d'évaluation différée). Ce Omnifarious se réfère également à la création de cette statique n'est pas thread-safe, sauf si vous utilisez gcc, qui a mis en œuvre le compilateur de manière spécifique pour s'assurer qu'il est thread-safe.
Je ne savais pas que sur les objets statiques à l'intérieur des fonctions. Mais ce que j'ai dit plus tôt ne vrai pour les objets statiques, a déclaré à la portée de fichier, n'est-ce pas?
Oui, c'est fait. Mais l'exemple étant reliée à ne pas utiliser un statique à la portée de fichier.
OriginalL'auteur Arkaitz Jimenez
Il est actuellement impossible paresseusement créer un Singleton en environnement multithread en C++.
Il a été reconnu par un certain nombre de gourous (parmi lesquels Herb Sutter), l'état actuel de la norme n'est pas une garantie de quoi que ce soit. Il existe des astuces pour une variété de compilateurs, et boost fournit la
once
facilité pour ce faire, cependant, c'est une collection hétéroclite de compilateurs des instructions spécifiques... ce n'est pas la norme C++ (qui est thread pas au courant).La seule solution actuellement en train de travailler (selon la norme) est d'initialiser le Singleton avant de lancer plusieurs threads ou dans une partie de la procédure qui garantit qu'un seul thread sera accédant.
C++0x apporte des fils dans le standard, et assure notamment que les collectivités locales de variables statiques seulement sera créé une fois que, même en présence de plusieurs threads (en cas de plusieurs appels simultanés de tous les bloquer jusqu'à ce que la création se termine). Donc la méthode suivante:
œuvres, et dans ce cas il n'y a pas besoin d'un singleton classe de modèle à tous.
OriginalL'auteur Matthieu M.