Rewrite Q_{GLOBAL,APPLICATION}_STATIC with C++17 goodies
Especially static inline variables. This greatly reduces the amount of code that existed in macros, moving them to templates. Additionally, this removes one level of indirection from Q_APPLICATION_STATIC by removing the std::unique_ptr. We now directly manage the object's storage. Change-Id: I2cffe62afda945079b63fffd16bcc825cc04334e Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
parent
fbbcd109f5
commit
81a31beeb2
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 Intel Corporation.
|
||||
** Copyright (C) 2021 Intel Corporation.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
@ -44,6 +44,7 @@
|
||||
|
||||
#include <QtCore/qatomic.h>
|
||||
|
||||
#include <atomic> // for bootstrapped (no thread) builds
|
||||
#include <type_traits>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -55,92 +56,92 @@ enum GuardValues {
|
||||
Uninitialized = 0,
|
||||
Initializing = 1
|
||||
};
|
||||
}
|
||||
|
||||
#define Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \
|
||||
struct HolderBase \
|
||||
{ \
|
||||
HolderBase() = default; \
|
||||
~HolderBase() noexcept \
|
||||
{ \
|
||||
if (guard.loadRelaxed() == QtGlobalStatic::Initialized) \
|
||||
guard.storeRelaxed(QtGlobalStatic::Destroyed); \
|
||||
} \
|
||||
Q_DISABLE_COPY_MOVE(HolderBase) \
|
||||
}; \
|
||||
struct Holder : public HolderBase \
|
||||
{ \
|
||||
Type value; \
|
||||
Holder() noexcept(noexcept(typename std::remove_cv<Type>::type ARGS)) \
|
||||
: value ARGS \
|
||||
{ \
|
||||
guard.storeRelaxed(QtGlobalStatic::Initialized); \
|
||||
} \
|
||||
};
|
||||
template <typename QGS> struct Holder
|
||||
{
|
||||
using Type = typename QGS::QGS_Type;
|
||||
using PlainType = std::remove_cv_t<Type>;
|
||||
|
||||
#if defined(Q_OS_UNIX) && defined(Q_CC_INTEL)
|
||||
// Work around Intel issue ID 6000058488:
|
||||
// local statics inside an inline function inside an anonymous namespace are global
|
||||
// symbols (this affects the IA-64 C++ ABI, so OS X and Linux only)
|
||||
# define Q_GLOBAL_STATIC_INTERNAL_DECORATION Q_DECL_HIDDEN
|
||||
#else
|
||||
# define Q_GLOBAL_STATIC_INTERNAL_DECORATION Q_DECL_HIDDEN inline
|
||||
#endif
|
||||
static constexpr bool ConstructionIsNoexcept = noexcept(QGS::innerFunction(nullptr));
|
||||
std::aligned_union_t<1, PlainType> storage;
|
||||
static inline QBasicAtomicInteger<qint8> guard = { QtGlobalStatic::Uninitialized };
|
||||
|
||||
#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \
|
||||
Q_GLOBAL_STATIC_INTERNAL_DECORATION Type *innerFunction() \
|
||||
{ \
|
||||
Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \
|
||||
static Holder holder; \
|
||||
return &holder.value; \
|
||||
Holder() noexcept(ConstructionIsNoexcept)
|
||||
{
|
||||
QGS::innerFunction(pointer());
|
||||
guard.storeRelaxed(QtGlobalStatic::Initialized);
|
||||
}
|
||||
|
||||
// this class must be POD, unless the compiler supports thread-safe statics
|
||||
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
|
||||
struct QGlobalStatic
|
||||
{
|
||||
typedef T Type;
|
||||
~Holder()
|
||||
{
|
||||
guard.storeRelaxed(QtGlobalStatic::Destroyed);
|
||||
std::atomic_thread_fence(std::memory_order_acquire); // avoid mixing stores to guard and *pointer()
|
||||
pointer()->~PlainType();
|
||||
}
|
||||
|
||||
bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; }
|
||||
bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; }
|
||||
PlainType *pointer() noexcept
|
||||
{
|
||||
return reinterpret_cast<PlainType *>(&storage);
|
||||
}
|
||||
|
||||
Q_DISABLE_COPY_MOVE(Holder)
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Holder> struct QGlobalStatic
|
||||
{
|
||||
using Type = typename Holder::Type;
|
||||
|
||||
bool isDestroyed() const { return guardValue() <= QtGlobalStatic::Destroyed; }
|
||||
bool exists() const { return guardValue() == QtGlobalStatic::Initialized; }
|
||||
operator Type *()
|
||||
{
|
||||
if (isDestroyed())
|
||||
return nullptr;
|
||||
return innerFunction();
|
||||
return instance();
|
||||
}
|
||||
Type *operator()()
|
||||
{
|
||||
if (isDestroyed())
|
||||
return nullptr;
|
||||
return innerFunction();
|
||||
return instance();
|
||||
}
|
||||
Type *operator->()
|
||||
{
|
||||
Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC",
|
||||
"The global static was used after being destroyed");
|
||||
return innerFunction();
|
||||
return instance();
|
||||
}
|
||||
Type &operator*()
|
||||
{
|
||||
Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC",
|
||||
"The global static was used after being destroyed");
|
||||
return *innerFunction();
|
||||
return *instance();
|
||||
}
|
||||
|
||||
protected:
|
||||
static Type *instance() noexcept(Holder::ConstructionIsNoexcept)
|
||||
{
|
||||
static Holder holder;
|
||||
return holder.pointer();
|
||||
}
|
||||
static QtGlobalStatic::GuardValues guardValue()
|
||||
{
|
||||
return QtGlobalStatic::GuardValues(Holder::guard.loadAcquire());
|
||||
}
|
||||
};
|
||||
|
||||
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
|
||||
QT_WARNING_PUSH \
|
||||
QT_WARNING_DISABLE_CLANG("-Wunevaluated-expression") \
|
||||
namespace { namespace Q_QGS_ ## NAME { \
|
||||
typedef TYPE Type; \
|
||||
QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
|
||||
Q_GLOBAL_STATIC_INTERNAL(ARGS) \
|
||||
} } \
|
||||
static QGlobalStatic<TYPE, \
|
||||
Q_QGS_ ## NAME::innerFunction, \
|
||||
Q_QGS_ ## NAME::guard> NAME; \
|
||||
QT_WARNING_POP
|
||||
namespace { struct Q_QGS_ ## NAME { \
|
||||
typedef TYPE QGS_Type; \
|
||||
static void innerFunction(void *pointer) \
|
||||
noexcept(noexcept(std::remove_cv_t<QGS_Type> ARGS)) \
|
||||
{ \
|
||||
new (pointer) QGS_Type ARGS; \
|
||||
} \
|
||||
}; } \
|
||||
static QGlobalStatic<QtGlobalStatic::Holder<Q_QGS_ ## NAME>> NAME; \
|
||||
/**/
|
||||
|
||||
#define Q_GLOBAL_STATIC(TYPE, NAME) \
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 Intel Corporation.
|
||||
** Copyright (C) 2021 Intel Corporation.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the documentation of the Qt Toolkit.
|
||||
@ -195,11 +195,11 @@
|
||||
\omit
|
||||
\section1 Compatibility with Qt 4 and Qt 5.0
|
||||
|
||||
This macro, in its current form and behavior, was introduced in Qt 5.1.
|
||||
Prior to that version, Qt had another macro with the same name that was
|
||||
private API. This section is not meant to document how to use
|
||||
Q_GLOBAL_STATIC in those versions, but instead to serve as a porting guide
|
||||
for Qt code that used those macros.
|
||||
This macro, in its current behavior, was introduced in Qt 5.1. Prior to
|
||||
that version, Qt had another macro with the same name that was private API.
|
||||
This section is not meant to document how to use Q_GLOBAL_STATIC in those
|
||||
versions, but instead to serve as a porting guide for Qt code that used
|
||||
those macros.
|
||||
|
||||
The Qt 4 Q_GLOBAL_STATIC macro differed in behavior in the following ways:
|
||||
|
||||
@ -218,24 +218,38 @@
|
||||
|
||||
\section1 Implementation Details
|
||||
|
||||
Q_GLOBAL_STATIC is implemented by creating a QBasicAtomicInt called the \c
|
||||
guard and a free, inline function called \c innerFunction. The guard
|
||||
variable is initialized to value 0 (chosen so that the guard can be placed
|
||||
in the .bss section of the binary file), which denotes that construction
|
||||
has not yet taken place (uninitialized). The inner function is implemented
|
||||
by the helper macro Q_GLOBAL_STATIC_INTERNAL.
|
||||
Q_GLOBAL_STATIC is implemented by creating a type called \c Q_QGS_NAME
|
||||
where \c NAME is the name of the variable the user passed to the macro,
|
||||
inside an unnamed namespace, and a \c static variable of \l QGlobalStatic
|
||||
type, named \c NAME. The use of unnamed namespaces forces the compiler to emit
|
||||
non-exported symbols, often local to the translation unit, and this
|
||||
propagates to the \l QGlobalStatic template instantiation that uses such
|
||||
types. Additionally, because the type is used only for one variable,
|
||||
there's effectively no difference between static and non-static data
|
||||
members.
|
||||
|
||||
Both the guard variable and the inner function are passed as template
|
||||
parameters to QGlobalStatic, along with the type \a Type. Both should also
|
||||
have static linkage or be placed inside an anonymous namespace, so that the
|
||||
visibility of Q_GLOBAL_STATIC is that of a global static. To permit
|
||||
multiple Q_GLOBAL_STATIC per translation unit, the guard variable and the
|
||||
inner function must have unique names, which can be accomplished by
|
||||
concatenating with \a VariableName or by placing them in a namespace that
|
||||
has \a VariableName as part of the name. To simplify and improve
|
||||
readability on Q_GLOBAL_STATIC_INTERNAL, we chose the namespace solution.
|
||||
It's also required for C++98 builds, since static symbols cannot be used as
|
||||
template parameters.
|
||||
The "QGS" type is a \c struct containing a \c typedef to \a TYPE and a
|
||||
static member function that receives a pointer to storage suitable to hold
|
||||
an instance of \a TYPE. It will initialize the storage using a placement \c
|
||||
new and the variadic arguments.
|
||||
|
||||
The majority of the work is done by the public \l QGlobalStatic class and
|
||||
the private \c QtGlobalStatic::Holder class. The \c Holder class has a
|
||||
non-static, trivial member of suitable size and alignment to hold \a TYPE
|
||||
(a \c{std::aligned_union_t} or a \c{std::aligned_storage_t}). The
|
||||
constructor calls the "QGS" type's static member function with a pointer to
|
||||
this location so it can be initialized and the destructor calls the type's
|
||||
destructor. The \c{Holder} type is therefore neither trivially
|
||||
constructible nor trivially destructible. It is used as a function-local \c
|
||||
static so its initialization is thread-safe due to C++11's requirement that
|
||||
such variables be thread-safely initialized.
|
||||
|
||||
Additionally, both the constructor and destructor modify a guard variable
|
||||
after construction and before destruction, respectively. The guard variable
|
||||
is implemented as a \c {static inline} member instead of a non-static
|
||||
member so the compiler and linker are free to place this variable in memory
|
||||
far from the actual object. This way, if we wanted to, we could mark it
|
||||
aligned-to-cacheline in the future to prevent false sharing.
|
||||
|
||||
The guard variable can assume the following values:
|
||||
|
||||
@ -257,34 +271,6 @@
|
||||
operate solely on the guard variable: the former returns \c true if the guard
|
||||
is negative, whereas the latter returns \c true only if it is -2.
|
||||
|
||||
The Q_GLOBAL_STATIC_INTERNAL macro implements the actual construction and
|
||||
destruction. There are two implementations of it: one for compilers that
|
||||
support thread-safe initialization of function-local statics and one for
|
||||
compilers that don't. Thread-safe initialization is required by C++11 in
|
||||
[stmt.decl], but as of the time of this writing, only compilers based on
|
||||
the IA-64 C++ ABI implemented it properly. The implementation requiring
|
||||
thread-safe initialization is also used on the Qt bootstrapped tools, which
|
||||
disable the "thread" feature.
|
||||
|
||||
The implementation requiring thread-safe initialization from the compiler
|
||||
is the simplest: it creates the \a Type object as a function-local static
|
||||
and returns its address. The actual object is actually inside a holder
|
||||
structure so holder's destructor can set the guard variable to the value -2
|
||||
(destroyed) when the type has finished destruction. Since we need to set
|
||||
the guard \b after the destruction has finished, this code needs to be in a
|
||||
base struct's destructor. And it only sets to -2 (destroyed) if it finds
|
||||
the guard at -1 (initialized): this is done to ensure that the guard isn't
|
||||
set to -2 in the event the type's constructor threw an exception. A holder
|
||||
structure is used to avoid creating two statics, which the ABI might
|
||||
require duplicating the thread-safe control structures for.
|
||||
|
||||
The other implementation is similar to Qt 4's Q_GLOBAL_STATIC, but unlike
|
||||
that one, it uses a \l QBasicMutex to provide locking. It is also more
|
||||
efficient memory-wise. It use a simple double-checked locking of the mutex
|
||||
and then creates the contents on the heap. After that, it creates a
|
||||
function-local structure called "Cleanup", whose destructor will be run at
|
||||
program exit and will actually destroy the contents.
|
||||
|
||||
\endomit
|
||||
|
||||
\sa Q_GLOBAL_STATIC_WITH_ARGS(), QGlobalStatic
|
||||
@ -356,7 +342,7 @@
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> bool QGlobalStatic<T, innerFunction, guard>::isDestroyed() const
|
||||
\fn template <typename Holder> bool QGlobalStatic<Holder>::isDestroyed() const
|
||||
|
||||
This function returns \c true if the global static object has already
|
||||
completed destruction (that is, if the destructor for the type has already
|
||||
@ -386,7 +372,7 @@
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> bool QGlobalStatic<T, innerFunction, guard>::exists() const
|
||||
\fn template <typename Holder> bool QGlobalStatic<Holder>::exists() const
|
||||
|
||||
This function returns \c true if the global static object has already
|
||||
completed initialization (that is, if the constructor for the type has
|
||||
@ -436,7 +422,7 @@
|
||||
|
||||
/*!
|
||||
\keyword qglobalstatic-operator-type-ptr
|
||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> QGlobalStatic<T, innerFunction, guard>::operator Type*()
|
||||
\fn template <typename Holder> QGlobalStatic<Holder>::operator Type*()
|
||||
|
||||
This function returns the address of the contents of this global static. If
|
||||
the contents have not yet been created, they will be created thread-safely
|
||||
@ -469,7 +455,7 @@
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> Type *QGlobalStatic<T, innerFunction, guard>::operator()()
|
||||
\fn template <typename Holder> Type *QGlobalStatic<Holder>::operator()()
|
||||
\deprecated
|
||||
|
||||
This function returns the address of the contents of this global static. If
|
||||
@ -485,7 +471,7 @@
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> Type *QGlobalStatic<T, innerFunction, guard>::operator->()
|
||||
\fn template <typename Holder> Type *QGlobalStatic<Holder>::operator->()
|
||||
|
||||
This function returns the address of the contents of this global static. If
|
||||
the contents have not yet been created, they will be created thread-safely
|
||||
@ -498,7 +484,7 @@
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> Type &QGlobalStatic<T, innerFunction, guard>::operator*()
|
||||
\fn template <typename Holder> Type &QGlobalStatic<Holder>::operator*()
|
||||
|
||||
This function returns a reference to the contents of this global static. If
|
||||
the contents have not yet been created, they will be created thread-safely
|
||||
|
@ -1,6 +1,7 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 The Qt Company Ltd.
|
||||
** Copyright (C) 2021 Intel Corporation.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
@ -41,58 +42,75 @@
|
||||
#define QAPPLICATIONSTATIC_H
|
||||
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qglobalstatic.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#define Q_APPLICATION_STATIC_INTERNAL_HOLDER(ARGS) \
|
||||
Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \
|
||||
struct LifecycleHolder : public Holder \
|
||||
{ \
|
||||
LifecycleHolder() { connectLifecycle(); } \
|
||||
BaseType *get() \
|
||||
{ \
|
||||
const QMutexLocker locker(&theMutex); \
|
||||
if (value.get() == nullptr \
|
||||
&& guard.loadRelaxed() == QtGlobalStatic::Initialized) { \
|
||||
value.reset(ARGS); \
|
||||
connectLifecycle(); \
|
||||
} \
|
||||
return value.get(); \
|
||||
} \
|
||||
void connectLifecycle() \
|
||||
{ \
|
||||
Q_ASSERT(QCoreApplication::instance()); \
|
||||
QObject::connect(QCoreApplication::instance(), \
|
||||
&QCoreApplication::destroyed, [this] { \
|
||||
const QMutexLocker locker(&theMutex); \
|
||||
value.reset(nullptr); \
|
||||
}); \
|
||||
} \
|
||||
};
|
||||
namespace QtGlobalStatic {
|
||||
template <typename QAS> struct ApplicationHolder
|
||||
{
|
||||
using Type = typename QAS::QAS_Type;
|
||||
using PlainType = std::remove_cv_t<Type>;
|
||||
|
||||
#define Q_APPLICATION_STATIC_INTERNAL(ARGS) \
|
||||
Q_GLOBAL_STATIC_INTERNAL_DECORATION BaseType *innerFunction() \
|
||||
{ \
|
||||
Q_APPLICATION_STATIC_INTERNAL_HOLDER(ARGS) \
|
||||
static LifecycleHolder holder; \
|
||||
return holder.get(); \
|
||||
static inline std::aligned_union_t<1, PlainType> storage;
|
||||
static inline QBasicAtomicInteger<qint8> guard = { QtGlobalStatic::Uninitialized };
|
||||
static inline QBasicMutex mutex {};
|
||||
|
||||
static constexpr bool MutexLockIsNoexcept = noexcept(mutex.lock());
|
||||
static constexpr bool ConstructionIsNoexcept = noexcept(QAS::innerFunction(nullptr));
|
||||
|
||||
ApplicationHolder() = default;
|
||||
Q_DISABLE_COPY_MOVE(ApplicationHolder)
|
||||
~ApplicationHolder()
|
||||
{
|
||||
if (guard.loadRelaxed() == QtGlobalStatic::Initialized) {
|
||||
guard.storeRelease(QtGlobalStatic::Destroyed);
|
||||
realPointer()->~PlainType();
|
||||
}
|
||||
}
|
||||
|
||||
static PlainType *realPointer()
|
||||
{
|
||||
return reinterpret_cast<PlainType *>(&storage);
|
||||
}
|
||||
|
||||
// called from QGlobalStatic::instance()
|
||||
PlainType *pointer() noexcept(MutexLockIsNoexcept && ConstructionIsNoexcept)
|
||||
{
|
||||
if (guard.loadRelaxed() == QtGlobalStatic::Initialized)
|
||||
return realPointer();
|
||||
QMutexLocker locker(&mutex);
|
||||
if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) {
|
||||
QAS::innerFunction(realPointer());
|
||||
QObject::connect(QCoreApplication::instance(), &QObject::destroyed, reset);
|
||||
guard.storeRelaxed(QtGlobalStatic::Initialized);
|
||||
}
|
||||
return realPointer();
|
||||
}
|
||||
|
||||
static void reset()
|
||||
{
|
||||
if (guard.loadRelaxed() == QtGlobalStatic::Initialized) {
|
||||
QMutexLocker locker(&mutex);
|
||||
realPointer()->~PlainType();
|
||||
guard.storeRelaxed(QtGlobalStatic::Uninitialized);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace QtGlobalStatic
|
||||
|
||||
#define Q_APPLICATION_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
|
||||
QT_WARNING_PUSH \
|
||||
QT_WARNING_DISABLE_CLANG("-Wunevaluated-expression") \
|
||||
namespace { namespace Q_QAS_ ## NAME { \
|
||||
typedef TYPE BaseType; \
|
||||
typedef std::unique_ptr<BaseType> Type; \
|
||||
QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
|
||||
QBasicMutex theMutex; \
|
||||
Q_APPLICATION_STATIC_INTERNAL((new BaseType ARGS)) \
|
||||
} } \
|
||||
static QGlobalStatic<TYPE, \
|
||||
Q_QAS_ ## NAME::innerFunction, \
|
||||
Q_QAS_ ## NAME::guard> NAME; \
|
||||
QT_WARNING_POP
|
||||
namespace { struct Q_QAS_ ## NAME { \
|
||||
typedef TYPE QAS_Type; \
|
||||
static void innerFunction(void *pointer) \
|
||||
noexcept(noexcept(std::remove_cv_t<QAS_Type> ARGS)) \
|
||||
{ \
|
||||
new (pointer) QAS_Type ARGS; \
|
||||
} \
|
||||
}; } \
|
||||
static QGlobalStatic<QtGlobalStatic::ApplicationHolder<Q_QAS_ ## NAME>> NAME;\
|
||||
/**/
|
||||
|
||||
#define Q_APPLICATION_STATIC(TYPE, NAME) \
|
||||
Q_APPLICATION_STATIC_WITH_ARGS(TYPE, NAME, ())
|
||||
|
@ -47,8 +47,10 @@
|
||||
type will be recreated when it's accessed again once a new QCoreApplication
|
||||
has been created.
|
||||
|
||||
Since the value is bound to the QCoreApplication it should only ever be accessed,
|
||||
if there is a valid QCoreApplication::instance().
|
||||
Since the value is bound to the QCoreApplication, it should only ever be
|
||||
accessed if there is a valid QCoreApplication::instance(). Accessing this
|
||||
object before QCoreApplication is created or after it's destroyed will
|
||||
produce warnings and may have unpredictable behavior.
|
||||
|
||||
The typical use of this macro is as follows, in a global context (that is,
|
||||
outside of any function bodies):
|
||||
@ -61,6 +63,42 @@
|
||||
this macro behaves identically to Q_GLOBAL_STATIC(). Please see that macro's
|
||||
documentation for more information.
|
||||
|
||||
\omit
|
||||
\section1 Implementation details
|
||||
See \l Q_GLOBAL_STATIC implementation details for an introduction.
|
||||
|
||||
Q_APPLICATION_STATIC uses the same \l QGlobalStatic public class that
|
||||
Q_GLOBAL_STATIC does, but instead uses a QtGlobalStatic::ApplicationHolder
|
||||
template class as the template parameter. The differences to
|
||||
QtGlobalStatic::Holder are:
|
||||
|
||||
\list
|
||||
\li The ApplicationHolder class is empty. Unlike Holder, the storage is
|
||||
provided as a \c {static inline} member, simply so that the static
|
||||
member reset() function can access it without having to save the
|
||||
pointer in a lambda.
|
||||
|
||||
\li The ApplicationHolder constructor is trivial; initialization of the
|
||||
type is instead deferred to the \c pointer() function. This means the
|
||||
C++11 thread-safe initialization of statics does not protect the
|
||||
object.
|
||||
|
||||
\li Instead, ApplicationHolder provides a mutex (implemented as a \c
|
||||
{static inline} member of type \l QBasicMutex) and locks it before
|
||||
constructing or destructing the object.
|
||||
|
||||
\li After constructing the object, it will QObject::connect() the
|
||||
QCoreApplication::destroyed() signal to a function that will in turn
|
||||
destroy the object.
|
||||
|
||||
\li The destructor will destroy the object if the application is
|
||||
exiting without first destroying the QCoreApplication object (i.e., a
|
||||
call to \c ::exit) or this Q_APPLICATION_STATIC is part of a plugin
|
||||
that is being unloaded.
|
||||
\endlist
|
||||
|
||||
\endomit
|
||||
|
||||
\sa Q_APPLICATION_STATIC_WITH_ARGS(), QGlobalStatic
|
||||
*/
|
||||
|
||||
|
@ -148,7 +148,7 @@ void tst_QGlobalStatic::exception()
|
||||
exceptionCaught = true;
|
||||
}
|
||||
QVERIFY(exceptionCaught);
|
||||
QCOMPARE(Q_QGS_throwingGS::guard.loadRelaxed(), 0);
|
||||
QCOMPARE(QtGlobalStatic::Holder<Q_QGS_throwingGS>::guard.loadRelaxed(), 0);
|
||||
QVERIFY(!throwingGS.exists());
|
||||
QVERIFY(!throwingGS.isDestroyed());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user