diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2016-06-07 12:30:28 +0100 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2016-08-02 23:03:15 +0000 |
commit | c0637c02980a20343f877a24869ec539d05e4546 (patch) | |
tree | 920ef98db1070f97042845877e2fe61caa27e241 /src/corelib/kernel | |
parent | 65f5a7558a14841e35ffbd9606ed55608c1e9439 (diff) |
QObject::connect: allow to disable narrowing of the connection arguments
One of the good features of the new connection style is that
implicit conversion is performed for the connection arguments.
However, this is also a bad feature when it comes to the old
C remnants in the C++ language: for instance, doubles implicitly
convert to ints, possibly losing precision (and GCC/Clang do not
even warn about those under -Wall, only MSVC does) or even
triggering undefined behavior.
For this reason, when using braced initialization, C++11
disables narrowing conversions or floating/integral conversions.
Use this feature when checking the arguments of a PMF-style
signal/slot connection. Technically this makes the program
ill-formed, however GCC still accepts it (but at least
warns under -Wall).
Hence, add a way to disable these implicit conversions.
This is a opt-in and guarded by a macro, as it's a source
incompatible change.
[ChangeLog][QtCore][QObject] The
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT macro has been added.
When using the new connection syntax (PMF-based) this macro
makes it illegal to narrow the arguments carried by the signal,
and/or to perform floating point to integral implicit
conversions on them. When the macro is defined,
depending on your compiler a QObject::connect() statement
triggering such conversions will now fail to compile.
Change-Id: Ie17eb3e66ce0cd780138e60d8bb7da815a4ada83
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/kernel')
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 13 | ||||
-rw-r--r-- | src/corelib/kernel/qobjectdefs_impl.h | 46 |
2 files changed, 59 insertions, 0 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index c846aada05..26c3ce2443 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4471,6 +4471,19 @@ QDebug operator<<(QDebug dbg, const QObject *o) */ /*! + \macro QT_NO_NARROWING_CONVERSIONS_IN_CONNECT + \relates QObject + \since 5.8 + + Defining this macro will disable narrowing and floating-point-to-integral + conversions between the arguments carried by a signal and the arguments + accepted by a slot, when the signal and the slot are connected using the + PMF-based syntax. + + \sa QObject::connect +*/ + +/*! \typedef QObjectList \relates QObject diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index 5eae70ecc5..e94e713e1f 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -50,6 +50,8 @@ #pragma qt_sync_stop_processing #endif +#include <type_traits> + QT_BEGIN_NAMESPACE @@ -194,6 +196,46 @@ namespace QtPrivate { }; /* + Logic that checks if the underlying type of an enum is signed or not. + Needs an external, explicit check that E is indeed an enum. Works + around the fact that it's undefined behavior to instantiate + std::underlying_type on non-enums (cf. §20.13.7.6 [meta.trans.other]). + */ + template<typename E, typename Enable = void> + struct IsEnumUnderlyingTypeSigned : std::false_type + { + }; + + template<typename E> + struct IsEnumUnderlyingTypeSigned<E, typename std::enable_if<std::is_enum<E>::value>::type> + : std::integral_constant<bool, std::is_signed<typename std::underlying_type<E>::type>::value> + { + }; + + /* + Logic that checks if the argument of the slot does not narrow the + argument of the signal when used in list initialization. Cf. §8.5.4.7 + [dcl.init.list] for the definition of narrowing. + For incomplete From/To types, there's no narrowing. + */ + template<typename From, typename To, typename Enable = void> + struct AreArgumentsNarrowedBase : std::false_type + { + }; + + template<typename From, typename To> + struct AreArgumentsNarrowedBase<From, To, typename std::enable_if<sizeof(From) && sizeof(To)>::type> + : std::integral_constant<bool, + (std::is_floating_point<From>::value && std::is_integral<To>::value) || + (std::is_floating_point<From>::value && std::is_floating_point<To>::value && sizeof(From) > sizeof(To)) || + ((std::is_integral<From>::value || std::is_enum<From>::value) && std::is_floating_point<To>::value) || + (std::is_integral<From>::value && std::is_integral<To>::value && (sizeof(From) > sizeof(To) || std::is_signed<From>::value != std::is_signed<To>::value)) || + (std::is_enum<From>::value && std::is_integral<To>::value && (sizeof(From) > sizeof(To) || IsEnumUnderlyingTypeSigned<From>::value != std::is_signed<To>::value)) + > + { + }; + + /* Logic that check if the arguments of the slot matches the argument of the signal. To be used like this: Q_STATIC_ASSERT(CheckCompatibleArguments<FunctionPointer<Signal>::Arguments, FunctionPointer<Slot>::Arguments>::value) @@ -203,6 +245,10 @@ namespace QtPrivate { static char test(...); static const typename RemoveRef<A1>::Type &dummy(); enum { value = sizeof(test(dummy())) == sizeof(int) }; +#ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT + struct AreArgumentsNarrowed : AreArgumentsNarrowedBase<typename RemoveRef<A1>::Type, typename RemoveRef<A2>::Type> {}; + Q_STATIC_ASSERT_X(!AreArgumentsNarrowed::value, "Signal and slot arguments are not compatible (narrowing)"); +#endif }; template<typename A1, typename A2> struct AreArgumentsCompatible<A1, A2&> { enum { value = false }; }; template<typename A> struct AreArgumentsCompatible<A&, A&> { enum { value = true }; }; |