Extract header qtclasshelpermacros.h

Move the class helper macros from qglobal.h and Q_DECLARE_SHARED from
qtypeinfo.h there.

Task-number: QTBUG-99313
Change-Id: I8c8b6241a76916176a48c09fbaf4effc87683770
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
This commit is contained in:
Sona Kurazyan 2022-08-12 16:36:54 +02:00
parent 23ecaf897f
commit c201c5aedf
6 changed files with 205 additions and 178 deletions

View File

@ -74,6 +74,7 @@ qt_internal_add_module(Core
global/qrandom.cpp global/qrandom.h global/qrandom_p.h global/qrandom.cpp global/qrandom.h global/qrandom_p.h
global/qsysinfo.h global/qsysinfo.h
global/qsystemdetection.h global/qsystemdetection.h
global/qtclasshelpermacros.h
global/qtconfigmacros.h global/qtconfigmacros.h
global/qtdeprecationmarkers.h global/qtdeprecationmarkers.h
global/qtrace_p.h global/qtrace_p.h

View File

@ -511,63 +511,6 @@ using namespace Qt::StringLiterals;
(bitmask). (bitmask).
*/ */
/*!
\macro Q_DISABLE_COPY(Class)
\relates QObject
Disables the use of copy constructors and assignment operators
for the given \a Class.
Instances of subclasses of QObject should not be thought of as
values that can be copied or assigned, but as unique identities.
This means that when you create your own subclass of QObject
(director or indirect), you should \e not give it a copy constructor
or an assignment operator. However, it may not enough to simply
omit them from your class, because, if you mistakenly write some code
that requires a copy constructor or an assignment operator (it's easy
to do), your compiler will thoughtfully create it for you. You must
do more.
The curious user will have seen that the Qt classes derived
from QObject typically include this macro in a private section:
\snippet code/src_corelib_global_qglobal.cpp 43
It declares a copy constructor and an assignment operator in the
private section, so that if you use them by mistake, the compiler
will report an error.
\snippet code/src_corelib_global_qglobal.cpp 44
But even this might not catch absolutely every case. You might be
tempted to do something like this:
\snippet code/src_corelib_global_qglobal.cpp 45
First of all, don't do that. Most compilers will generate code that
uses the copy constructor, so the privacy violation error will be
reported, but your C++ compiler is not required to generate code for
this statement in a specific way. It could generate code using
\e{neither} the copy constructor \e{nor} the assignment operator we
made private. In that case, no error would be reported, but your
application would probably crash when you called a member function
of \c{w}.
\sa Q_DISABLE_COPY_MOVE
*/
/*!
\macro Q_DISABLE_COPY_MOVE(Class)
\relates QObject
A convenience macro that disables the use of copy constructors, assignment
operators, move constructors and move assignment operators for the given
\a Class.
\sa Q_DISABLE_COPY
\since 5.13
*/
/*! /*!
\macro Q_DECLARE_FLAGS(Flags, Enum) \macro Q_DECLARE_FLAGS(Flags, Enum)
\relates QFlags \relates QFlags

View File

@ -46,6 +46,7 @@ inline void qt_noop(void) {}
#include <QtCore/qassert.h> #include <QtCore/qassert.h>
#include <QtCore/qtypes.h> #include <QtCore/qtypes.h>
#include <QtCore/qtclasshelpermacros.h>
/* /*
Avoid "unused parameter" warnings Avoid "unused parameter" warnings
@ -55,65 +56,6 @@ inline void qt_noop(void) {}
#ifndef __ASSEMBLER__ #ifndef __ASSEMBLER__
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
/*
Some classes do not permit copies to be made of an object. These
classes contains a private copy constructor and assignment
operator to disable copying (the compiler gives an error message).
*/
#define Q_DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
/*
Implementing a move assignment operator using an established
technique (move-and-swap, pure swap) is just boilerplate.
Here's a couple of *private* macros for convenience.
To know which one to use:
* if you don't have a move constructor (*) => use pure swap;
* if you have a move constructor, then
* if your class holds just memory (no file handles, no user-defined
datatypes, etc.) => use pure swap;
* use move and swap.
The preference should always go for the move-and-swap one, as it
will deterministically destroy the data previously held in *this,
and not "dump" it in the moved-from object (which may then be alive
for longer).
The requirement for either macro is the presence of a member swap(),
which any value class that defines its own special member functions
should have anyhow.
(*) Many value classes in Qt do not have move constructors; mostly,
the implicitly shared classes using QSharedDataPointer and friends.
The reason is mostly historical: those classes require either an
out-of-line move constructor, which we could not provide before we
made C++11 mandatory (and that we don't like anyhow), or
an out-of-line dtor for the Q(E)DSP<Private> member (cf. QPixmap).
If you can however add a move constructor to a class lacking it,
consider doing so, then reevaluate which macro to choose.
*/
#define QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(Class) \
Class &operator=(Class &&other) noexcept { \
Class moved(std::move(other)); \
swap(moved); \
return *this; \
}
#define QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(Class) \
Class &operator=(Class &&other) noexcept { \
swap(other); \
return *this; \
}
/* /*
No, this is not an evil backdoor. QT_BUILD_INTERNAL just exports more symbols No, this is not an evil backdoor. QT_BUILD_INTERNAL just exports more symbols
for Qt's internal unit tests. If you want slower loading times and more for Qt's internal unit tests. If you want slower loading times and more
@ -244,26 +186,6 @@ typedef void (*QFunctionPointer)();
# define Q_UNIMPLEMENTED() qWarning("Unimplemented code.") # define Q_UNIMPLEMENTED() qWarning("Unimplemented code.")
#endif #endif
/*
Compilers which follow outdated template instantiation rules
require a class to have a comparison operator to exist when
a QList of this type is instantiated. It's not actually
used in the list, though. Hence the dummy implementation.
Just in case other code relies on it we better trigger a warning
mandating a real implementation.
*/
#ifdef Q_FULL_TEMPLATE_INSTANTIATION
# define Q_DUMMY_COMPARISON_OPERATOR(C) \
bool operator==(const C&) const { \
qWarning(#C"::operator==(const "#C"&) was called"); \
return false; \
}
#else
# define Q_DUMMY_COMPARISON_OPERATOR(C)
#endif
QT_WARNING_PUSH QT_WARNING_PUSH
// warning: noexcept-expression evaluates to 'false' because of a call to 'void swap(..., ...)' // warning: noexcept-expression evaluates to 'false' because of a call to 'void swap(..., ...)'
QT_WARNING_DISABLE_GCC("-Wnoexcept") QT_WARNING_DISABLE_GCC("-Wnoexcept")
@ -328,33 +250,8 @@ constexpr std::underlying_type_t<Enum> qToUnderlying(Enum e) noexcept
return static_cast<std::underlying_type_t<Enum>>(e); return static_cast<std::underlying_type_t<Enum>>(e);
} }
template <typename T> inline T *qGetPtrHelper(T *ptr) noexcept { return ptr; }
template <typename Ptr> inline auto qGetPtrHelper(Ptr &ptr) noexcept -> decltype(ptr.get())
{ static_assert(noexcept(ptr.get()), "Smart d pointers for Q_DECLARE_PRIVATE must have noexcept get()"); return ptr.get(); }
// The body must be a statement: // The body must be a statement:
#define Q_CAST_IGNORE_ALIGN(body) QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wcast-align") body QT_WARNING_POP #define Q_CAST_IGNORE_ALIGN(body) QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wcast-align") body QT_WARNING_POP
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() noexcept \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));) } \
inline const Class##Private* d_func() const noexcept \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));) } \
friend class Class##Private;
#define Q_DECLARE_PRIVATE_D(Dptr, Class) \
inline Class##Private* d_func() noexcept \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(Dptr));) } \
inline const Class##Private* d_func() const noexcept \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(Dptr));) } \
friend class Class##Private;
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() noexcept { return static_cast<Class *>(q_ptr); } \
inline const Class* q_func() const noexcept { return static_cast<const Class *>(q_ptr); } \
friend class Class;
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
#define QT_MODULE(x) #define QT_MODULE(x)

View File

@ -0,0 +1,144 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTCLASSHELPERMACROS_H
#define QTCLASSHELPERMACROS_H
#include <QtCore/qtconfigmacros.h>
#if 0
#pragma qt_class(QtClassHelperMacros)
#pragma qt_sync_stop_processing
#endif
QT_BEGIN_NAMESPACE
#if defined(__cplusplus)
/*
Some classes do not permit copies to be made of an object. These
classes contains a private copy constructor and assignment
operator to disable copying (the compiler gives an error message).
*/
#define Q_DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
/*
Implementing a move assignment operator using an established
technique (move-and-swap, pure swap) is just boilerplate.
Here's a couple of *private* macros for convenience.
To know which one to use:
* if you don't have a move constructor (*) => use pure swap;
* if you have a move constructor, then
* if your class holds just memory (no file handles, no user-defined
datatypes, etc.) => use pure swap;
* use move and swap.
The preference should always go for the move-and-swap one, as it
will deterministically destroy the data previously held in *this,
and not "dump" it in the moved-from object (which may then be alive
for longer).
The requirement for either macro is the presence of a member swap(),
which any value class that defines its own special member functions
should have anyhow.
(*) Many value classes in Qt do not have move constructors; mostly,
the implicitly shared classes using QSharedDataPointer and friends.
The reason is mostly historical: those classes require either an
out-of-line move constructor, which we could not provide before we
made C++11 mandatory (and that we don't like anyhow), or
an out-of-line dtor for the Q(E)DSP<Private> member (cf. QPixmap).
If you can however add a move constructor to a class lacking it,
consider doing so, then reevaluate which macro to choose.
*/
#define QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(Class) \
Class &operator=(Class &&other) noexcept { \
Class moved(std::move(other)); \
swap(moved); \
return *this; \
}
#define QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(Class) \
Class &operator=(Class &&other) noexcept { \
swap(other); \
return *this; \
}
template <typename T> inline T *qGetPtrHelper(T *ptr) noexcept { return ptr; }
template <typename Ptr> inline auto qGetPtrHelper(Ptr &ptr) noexcept -> decltype(ptr.get())
{ static_assert(noexcept(ptr.get()), "Smart d pointers for Q_DECLARE_PRIVATE must have noexcept get()"); return ptr.get(); }
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() noexcept \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));) } \
inline const Class##Private* d_func() const noexcept \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));) } \
friend class Class##Private;
#define Q_DECLARE_PRIVATE_D(Dptr, Class) \
inline Class##Private* d_func() noexcept \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(Dptr));) } \
inline const Class##Private* d_func() const noexcept \
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(Dptr));) } \
friend class Class##Private;
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() noexcept { return static_cast<Class *>(q_ptr); } \
inline const Class* q_func() const noexcept { return static_cast<const Class *>(q_ptr); } \
friend class Class;
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
/*
Compilers which follow outdated template instantiation rules
require a class to have a comparison operator to exist when
a QList of this type is instantiated. It's not actually
used in the list, though. Hence the dummy implementation.
Just in case other code relies on it we better trigger a warning
mandating a real implementation.
*/
#ifdef Q_FULL_TEMPLATE_INSTANTIATION
# define Q_DUMMY_COMPARISON_OPERATOR(C) \
bool operator==(const C&) const { \
qWarning(#C"::operator==(const "#C"&) was called"); \
return false; \
}
#else
# define Q_DUMMY_COMPARISON_OPERATOR(C)
#endif
/*
Specialize a shared type with:
Q_DECLARE_SHARED(type)
where 'type' is the name of the type to specialize. NOTE: shared
types must define a member-swap, and be defined in the same
namespace as Qt for this to work.
*/
#define Q_DECLARE_SHARED_IMPL(TYPE, FLAGS) \
Q_DECLARE_TYPEINFO(TYPE, FLAGS); \
inline void swap(TYPE &value1, TYPE &value2) \
noexcept(noexcept(value1.swap(value2))) \
{ value1.swap(value2); }
#define Q_DECLARE_SHARED(TYPE) Q_DECLARE_SHARED_IMPL(TYPE, Q_RELOCATABLE_TYPE)
#endif // __cplusplus
QT_END_NAMESPACE
#endif // QTCLASSHELPERMACROS_H

View File

@ -0,0 +1,59 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\macro Q_DISABLE_COPY(Class)
\relates QtClassHelperMacros
Disables the use of copy constructors and assignment operators
for the given \a Class.
Instances of subclasses of QObject should not be thought of as
values that can be copied or assigned, but as unique identities.
This means that when you create your own subclass of QObject
(director or indirect), you should \e not give it a copy constructor
or an assignment operator. However, it may not enough to simply
omit them from your class, because, if you mistakenly write some code
that requires a copy constructor or an assignment operator (it's easy
to do), your compiler will thoughtfully create it for you. You must
do more.
The curious user will have seen that the Qt classes derived
from QObject typically include this macro in a private section:
\snippet code/src_corelib_global_qglobal.cpp 43
It declares a copy constructor and an assignment operator in the
private section, so that if you use them by mistake, the compiler
will report an error.
\snippet code/src_corelib_global_qglobal.cpp 44
But even this might not catch absolutely every case. You might be
tempted to do something like this:
\snippet code/src_corelib_global_qglobal.cpp 45
First of all, don't do that. Most compilers will generate code that
uses the copy constructor, so the privacy violation error will be
reported, but your C++ compiler is not required to generate code for
this statement in a specific way. It could generate code using
\e{neither} the copy constructor \e{nor} the assignment operator we
made private. In that case, no error would be reported, but your
application would probably crash when you called a member function
of \c{w}.
\sa Q_DISABLE_COPY_MOVE
*/
/*!
\macro Q_DISABLE_COPY_MOVE(Class)
\relates QtClassHelperMacros
A convenience macro that disables the use of copy constructors, assignment
operators, move constructors and move assignment operators for the given
\a Class.
\sa Q_DISABLE_COPY
\since 5.13
*/

View File

@ -143,23 +143,6 @@ template<typename T> class QFlags;
template<typename T> template<typename T>
Q_DECLARE_TYPEINFO_BODY(QFlags<T>, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO_BODY(QFlags<T>, Q_PRIMITIVE_TYPE);
/*
Specialize a shared type with:
Q_DECLARE_SHARED(type)
where 'type' is the name of the type to specialize. NOTE: shared
types must define a member-swap, and be defined in the same
namespace as Qt for this to work.
*/
#define Q_DECLARE_SHARED_IMPL(TYPE, FLAGS) \
Q_DECLARE_TYPEINFO(TYPE, FLAGS); \
inline void swap(TYPE &value1, TYPE &value2) \
noexcept(noexcept(value1.swap(value2))) \
{ value1.swap(value2); }
#define Q_DECLARE_SHARED(TYPE) Q_DECLARE_SHARED_IMPL(TYPE, Q_RELOCATABLE_TYPE)
namespace QTypeTraits namespace QTypeTraits
{ {