C++ singleton-template-Klasse
In einem kürzlichen Projekt hatte ich das erstellen einer Singleton-Klasse und nach viel Graben, um auf Google kam ich mit dieser template-Klasse definiert. Die Idee ist die Ableitung von dieser template-Klasse und machen der abgeleiteten Klasse der Konstruktor protected /private. Es scheint gut zu funktionieren, aber ich habe nur verwendet es mit einer einzigen Klasse in einem Projekt, so hoffte ich, einige von Euch könnten darauf hinweisen, wenn ich Fehler gemacht habe bei der Umsetzung. Hier ist es:
/**
* @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;
Ich bin besonders besorgt über die statischen member-Initialisierungen in der header-Datei und ob das wird dazu führen, dass mehrere definition-Fehler, wenn die header-Datei der abgeleiteten Klasse aus der SingleDynamic enthalten ist, in mehreren Dateien. Habe ich schon ausprobiert und es scheint zu funktionieren, aber ich kann nicht herausfinden, warum seine arbeiten :).
Vielen Dank im Voraus,
Ashish.
EDIT: Geändert Implementierung die Verwendung einer policy-based design wie bereits in der akzeptierten Lösung.
/**
* 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;
Ich Stimme mit GMan in nicht verwenden singeltons. Ich bin nicht einverstanden, dass Sie das gleiche wie globals (lazy initialization). Und ich hasse, mit Zeigern, wie es die Darstellung, da diese nicht automatisch gelöscht werden (verwenden Sie eine statische variable, Funktion innerhalb get_instance() (sperren), die Art und Weise der singleton richtig gelöscht werden). PS. Sie müssen sich zu bewegen
flag_ = true;
im inneren der Schlösser sonst könnte Euch mehrere threads erstellen der Instanz.Auch Sie können zu stehlen den kreativen Schöpfung Lösungen singletons verwenden, um eine schöne Globale utility-Bibliothek. Es ist vor allem die eingeschränkte Instanz Scheiße, das ist Verschleierung und unnötig. Ich überlege mir die Einreichung einer globalen Bibliothek zu Steigern, da es fehlt.
Es spielt keine Rolle, dass
flag_ = true;
außerhalb des mutex-locks, nur ein thread über den (*SingletonDynamic< T >::pfnLockMutex)();
und dieser thread ändern Sie die pInstance_
variable ist also nicht NULL. Dies wird verhindern, dass ein thread wartet auf die Sperre von dem erstellen einer neuen Instanz.Sind Sie richtig. Hoppla.
InformationsquelleAutor Praetorian | 2010-08-09
Du musst angemeldet sein, um einen Kommentar abzugeben.
Die Richtigkeit der Parallelität bezogene code ist hier schwer zu beurteilen. Die Umsetzung wird versucht, ein wenig zu clever, meiner Meinung nach.
OTOH, alle die Parallelität im Zusammenhang mit code hat im Grunde stubs dahinter, die nichts zu tun. Wenn dieses verwendet wird, in einem non-threaded Umgebung, ich denke, es sollte in Ordnung sein.
Aber, ich denke auch, deine Sorge ist gut begründet. Diese externen Definitionen der statischen Elemente scheinen, wie Sie würde gegen die one definition rule.
Ich persönlich glaube, dass diese Vorlage sollte neu geschrieben werden, um die Parallelität Zeug als politisches argument, um die Vorlage selbst, und zu verlangen, abgeleitete Klassen zu erklären, Ihre eigenen Versionen von
pInstance
im eine entsprechende .cpp-Datei.Jemand anderes vorgeschlagen hat, sich auf compiler-spezifisches Verhalten im Hinblick auf die Initialisierung von statischen lokalen Variablen. Ich glaube nicht, dass das ein schrecklicher Vorschlag, aber es wäre schön, eine option wenn Sie kann nicht darauf verlassen, dass der compiler das richtige zu tun.
InformationsquelleAutor Omnifarious
Statische member des template-Klassen initialisiert werden müssen, die in der header-Datei und die jüngsten C++ - Compiler und-Ihre-linkers müssen diese richtig verarbeiten.
Aber du hast Recht, einige sehr alte compiler haben Probleme mit diesem.
In diesen Fällen ist es eine Problemumgehung, die zum initialisieren der statischen Elemente genau einmal in einer beliebigen Zusammenstellung Gerät für jede Art der singleton-template verwendet wird.
Den gcc-Dokumentation hat die gleichen details über diese: http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html.
Ich erinnere mich an ein embedded-Projekt (nicht lange her), wo eine alte compiler war noch im Einsatz und man schweigend erstellt mehrere Instanzen von template die statischen Mitglieder.
Offensichtlich eine sehr schlechte Idee, wenn es gekommen ist, zum speichern eines Singleton....
Schlimmer noch, die einzige Singleton zu verwenden, in der Bibliothek (3rd-party-framework) wurde einige Konfigurations-Objekt, das in der Regel initialisiert wurde in der gleichen Weise, so dass der Fehler nur auftrat, wenn die Konfiguration zur Laufzeit geändert.
Es dauerte mehrere Tage, um den bug nach unten, bis wir endlich sahen, bei der Demontage, dass die "gleichen" - Mitglied wird der Zugriff auf verschiedene Speicherbereiche.
InformationsquelleAutor IanH
Muss nicht sein, dass-Komplex.
Einfache C++ - logger durch die Verwendung des singleton-Musters
den link oben benutzt nur eine statische singleton-Objekt. Warum ist das compiler-spezifisch ist? Ist es nicht wahr für alle Compiler, alle statischen Objekte sind konstruiert, bevor
main()
führt? So dass mit der Umsetzung sollte thread-sicher, solange alle Ihre aufrufenden threads, die Ausführung nachmain()
beginnt mit der Ausführung.Nein. statische Funktion Objekte werden nur erstellt bei der ersten Verwendung (erlaubt lazy evaluation). Was Omnifarious bezieht sich auch, dass die Schaffung dieses statische ist nicht thread-sicher, wenn Sie gcc verwenden, die implementiert wurde der compiler in einer bestimmten Weise, um sicherzustellen, es ist thread-sicher.
Ich wusste nicht, dass über die static Objekte innerhalb von Funktionen. Aber was ich früher gesagt habe nicht wahr halten für statische Objekte, deklariert in der Datei scope, nicht wahr?
Ja, tut es. Aber das Beispiel wird im Zusammenhang mit nicht eine statische dateigültigkeitsbereich.
InformationsquelleAutor Arkaitz Jimenez
Es ist derzeit unmöglich zu träge erstellen einer Singleton in einer Multithread-Umgebung in C++.
Es ist anerkannt worden, indem eine Reihe von gurus (zu denen Herb Sutter), dass der aktuelle Stand der standard hat nichts garantieren. Gibt es hacks für eine Vielzahl von Compilern und boost liefert die
once
Einrichtung für diesen Zweck, aber es ist eine bunte Sammlung von Compiler-spezifischen Anweisungen... es ist nicht standard-C++ (die Gewinde nicht bewusst).Die einzige Lösung arbeitet derzeit (nach der standard) ist die Initialisierung der Singleton-vor dem Start der mehrere threads, oder in einem Teil des Prozesses, der garantiert nur ein thread drauf zugreift.
C++0x bringt die threads in den standard-und vor allem garantiert, dass lokale statische Variablen werden nur einmal angelegt werden, auch in Anwesenheit von mehreren threads (im Falle von mehreren gleichzeitigen Anrufe alle blockieren, bis die Schöpfung endet). Deshalb ist die folgende Methode:
funktioniert, und in diesem Fall gibt es keine Notwendigkeit für ein singleton-template-Klasse überhaupt.
InformationsquelleAutor Matthieu M.