From 73a5ce5195bc27acf02d787787a69ec493c037c2 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 2 Mar 2012 11:51:52 +0100 Subject: [PATCH] QVariant: fix HasIsNullMethod for final classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HasIsNullMethod uses the accepted C++98 idiom to check for members that might be inherited from baseclasses. The technique, however, requires inheriting from the type-under-test, which fails for C++11 final classes. Fortunately, under C++11 we have much better support for static type introspection: sfinae for expressions. We use this here (see decltype() use in qvariant_p.h) to write a C++11 version of HasIsNullMethod that works with final classes, too. However, since this technique required decltype() support in the compiler, Q_DECL_FINAL can no longer be used for both method and class markup. So we declare a new Q_DECL_FINAL_CLASS which is only set iff the compiler supports decltype(), too. MSVC 2005 and 2008 support a non-standard, but sufficiently compatible, version of override/final, but no decltype(). A later patch will use MSVC 'override/'sealed' to implement Q_DECL_{OVERRIDE,FINAL} for these compilers, but I currently don't see a version of HasIsNullMethod that could support these two, so the split off of Q_DECL_FINAL_CLASS is in anticipation of that commit. If someone _does_ find an implementation of HasIsNullMethod that works on MSVC2005 and 2008 sealed classes, then it's a simple matter of s/Q_DECL_FINAL_CLASS/Q_DECL_FINAL/g. This code has been tested on GCC 4.7 (prerelease) and GCC 4.8 (prerelease). Change-Id: I8700c8307d79a74d45fef0aec1c6027b4a922a43 Reviewed-by: Jędrzej Nowacki Reviewed-by: Olivier Goffart --- src/corelib/global/qglobal.h | 6 ++++++ src/corelib/kernel/qvariant_p.h | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 99328d52ac..d0d6e851ad 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -415,9 +415,15 @@ QT_END_INCLUDE_NAMESPACE #ifdef Q_COMPILER_EXPLICIT_OVERRIDES # define Q_DECL_OVERRIDE override # define Q_DECL_FINAL final +# ifdef Q_COMPILER_DECLTYPE // required for class-level final to compile in qvariant_p.h +# define Q_DECL_FINAL_CLASS final +# else +# define Q_DECL_FINAL_CLASS +# endif #else # define Q_DECL_OVERRIDE # define Q_DECL_FINAL +# define Q_DECL_FINAL_CLASS #endif //defines the type for the WNDPROC on windows diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index a754bc4363..65d346c470 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -203,6 +203,19 @@ class QVariantIsNull /// \internal /// This class checks if a type T has method called isNull. Result is kept in the Value property /// TODO Can we somehow generalize it? A macro version? +#if defined(Q_COMPILER_DECLTYPE) // C++11 version + template + class HasIsNullMethod { + struct Yes { char unused[1]; }; + struct No { char unused[2]; }; + Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No)); + + template static decltype(static_cast(0)->isNull(), Yes()) test(int); + template static No test(...); + public: + static const bool Value = (sizeof(test(0)) == sizeof(Yes)); + }; +#else // C++98 version (doesn't work for final classes) template::isComplex> class HasIsNullMethod { @@ -211,7 +224,7 @@ class QVariantIsNull Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No)); struct FallbackMixin { bool isNull() const; }; - struct Derived : public T, public FallbackMixin {}; + struct Derived : public T, public FallbackMixin {}; // <- doesn't work for final classes template struct TypeCheck {}; template static Yes test(...); @@ -227,6 +240,7 @@ class QVariantIsNull public: static const bool Value = false; }; +#endif // TODO This part should go to autotests during HasIsNullMethod generalization. Q_STATIC_ASSERT(!HasIsNullMethod::Value); @@ -236,6 +250,12 @@ class QVariantIsNull Q_STATIC_ASSERT(!HasIsNullMethod::Value); struct SelfTest3 : public SelfTest1 {}; Q_STATIC_ASSERT(HasIsNullMethod::Value); + struct SelfTestFinal1 Q_DECL_FINAL_CLASS { bool isNull() const; }; + Q_STATIC_ASSERT(HasIsNullMethod::Value); + struct SelfTestFinal2 Q_DECL_FINAL_CLASS {}; + Q_STATIC_ASSERT(!HasIsNullMethod::Value); + struct SelfTestFinal3 Q_DECL_FINAL_CLASS : public SelfTest1 {}; + Q_STATIC_ASSERT(HasIsNullMethod::Value); template::Value> struct CallFilteredIsNull