Improve error reporting when requesting unsupported native interface
By switching out the static_assert for an enable_if we end up producing a clearer error, at the call site: /qt/qtbase/examples/gui/rasterwindow/main.cpp:69:9: error: no matching member function for call to 'nativeInterface' app.nativeInterface<QNativeInterface::QCocoaGLContext>(); ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /qt/qtbase/src/gui/kernel/qguiapplication.h:176:5: note: candidate template ignored: requirement 'NativeInterface<QNativeInterface::QCocoaGLContext>::isCompatibleWith<QGuiApplication>' was not satisfied [with NativeInterface = QNativeInterface::QCocoaGLContext, TypeInfo = QNativeInterface::Private::NativeInterface<QNativeInterface::QCocoaGLContext>, BaseType = QGuiApplication] QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QGuiApplication) ^ By using SFINAE for the TypeInfo we can also ensure that it works for types that are not native interfaces, such as if the user tries to call nativeInterface<QString>(). Since we can no longer use decltype(*this) to resolve the base type we need to change QT_DECLARE_NATIVE_INTERFACE_ACCESSOR to take the type as an argument, as we do for other QT_DECLARE_FOO macros. Pick-to: 6.2 Change-Id: Ie3f7e01ab7c3eb3dcc2ef730834f268bb9e81e0c Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
149b5425d8
commit
1ef305de15
@ -61,11 +61,21 @@ QT_BEGIN_NAMESPACE
|
||||
#define QT_DECLARE_NATIVE_INTERFACE_3(NativeInterface, Revision, BaseType) \
|
||||
protected: \
|
||||
virtual ~NativeInterface(); \
|
||||
\
|
||||
struct TypeInfo { \
|
||||
using baseType = BaseType; \
|
||||
static constexpr char const *name = QT_STRINGIFY(NativeInterface); \
|
||||
static constexpr int revision = Revision; \
|
||||
}; \
|
||||
\
|
||||
template <typename, typename> \
|
||||
friend struct QNativeInterface::Private::has_type_info; \
|
||||
\
|
||||
template <typename> \
|
||||
friend bool constexpr QNativeInterface::Private::hasTypeInfo(); \
|
||||
\
|
||||
template <typename> \
|
||||
friend struct QNativeInterface::Private::TypeInfo; \
|
||||
public: \
|
||||
|
||||
// Revisioned interfaces only make sense when exposed through a base
|
||||
@ -81,35 +91,97 @@ QT_BEGIN_NAMESPACE
|
||||
QT_OVERLOADED_MACRO(QT_DECLARE_NATIVE_INTERFACE, __VA_ARGS__)
|
||||
|
||||
namespace QNativeInterface::Private {
|
||||
template <typename NativeInterface>
|
||||
struct TypeInfo : private NativeInterface
|
||||
{
|
||||
static constexpr char const *name() { return NativeInterface::TypeInfo::name; }
|
||||
static constexpr int revision() { return NativeInterface::TypeInfo::revision; }
|
||||
|
||||
template<typename BaseType>
|
||||
static constexpr bool isCompatibleWith =
|
||||
std::is_base_of<typename NativeInterface::TypeInfo::baseType, BaseType>::value;
|
||||
// Basic type-trait to verify that a given native interface has
|
||||
// all the required type information for us to evaluate it.
|
||||
template <typename NativeInterface, typename = void>
|
||||
struct has_type_info : std::false_type {};
|
||||
|
||||
// The type-trait is friended by TypeInfo, so that we can
|
||||
// evaluate TypeInfo in the template arguments.
|
||||
template <typename NativeInterface>
|
||||
struct has_type_info<NativeInterface, std::void_t<
|
||||
typename NativeInterface::TypeInfo,
|
||||
typename NativeInterface::TypeInfo::baseType,
|
||||
decltype(&NativeInterface::TypeInfo::name),
|
||||
decltype(&NativeInterface::TypeInfo::revision)
|
||||
>> : std::true_type {};
|
||||
|
||||
// We need to wrap the instantiation of has_type_info in a
|
||||
// function friended by TypeInfo, otherwise MSVC will not
|
||||
// let us evaluate TypeInfo in the template arguments.
|
||||
template <typename NativeInterface>
|
||||
bool constexpr hasTypeInfo()
|
||||
{
|
||||
return has_type_info<NativeInterface>::value;
|
||||
}
|
||||
|
||||
template <typename NativeInterface>
|
||||
struct TypeInfo
|
||||
{
|
||||
// To ensure SFINAE works for hasTypeInfo we can't use it in a constexpr
|
||||
// variable that also includes an expression that relies on the type
|
||||
// info. This helper variable is okey, as it it self contained.
|
||||
static constexpr bool haveTypeInfo = hasTypeInfo<NativeInterface>();
|
||||
|
||||
// We can then use the helper variable in a constexpr condition in a
|
||||
// function, which does not break SFINAE if haveTypeInfo is false.
|
||||
template <typename BaseType>
|
||||
static constexpr bool isCompatibleHelper()
|
||||
{
|
||||
if constexpr (haveTypeInfo)
|
||||
return std::is_base_of<typename NativeInterface::TypeInfo::baseType, BaseType>::value;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// MSVC however doesn't like constexpr functions in enable_if_t conditions,
|
||||
// so we need to wrap it yet again in a constexpr variable. This is fine,
|
||||
// as all the SFINAE magic has been resolved at this point.
|
||||
template <typename BaseType>
|
||||
static constexpr bool isCompatibleWith = isCompatibleHelper<BaseType>();
|
||||
|
||||
// The revision and name accessors are not used in enable_if_t conditions,
|
||||
// so we can leave them as constexpr functions. As this class template is
|
||||
// friended by TypeInfo we can access the protected members of TypeInfo.
|
||||
static constexpr int revision()
|
||||
{
|
||||
if constexpr (haveTypeInfo)
|
||||
return NativeInterface::TypeInfo::revision;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static constexpr char const *name()
|
||||
{
|
||||
if constexpr (haveTypeInfo)
|
||||
return NativeInterface::TypeInfo::name;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper type to make the error message in case
|
||||
// of incompatible interface types read better.
|
||||
template <typename I>
|
||||
struct NativeInterface : TypeInfo<I> {};
|
||||
|
||||
template <typename T>
|
||||
Q_NATIVE_INTERFACE_IMPORT void *resolveInterface(const T *that, const char *name, int revision);
|
||||
|
||||
Q_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcNativeInterface)
|
||||
}
|
||||
|
||||
} // QNativeInterface::Private
|
||||
|
||||
// Declares an accessor for the native interface
|
||||
#define QT_DECLARE_NATIVE_INTERFACE_ACCESSOR \
|
||||
template <typename I> \
|
||||
I *nativeInterface() const \
|
||||
#define QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(T) \
|
||||
template <typename NativeInterface, typename TypeInfo = QNativeInterface::Private::NativeInterface<NativeInterface>, \
|
||||
typename BaseType = T, std::enable_if_t<TypeInfo::template isCompatibleWith<T>, bool> = true> \
|
||||
NativeInterface *nativeInterface() const \
|
||||
{ \
|
||||
using T = std::decay_t<decltype(*this)>; \
|
||||
using NativeInterface = QNativeInterface::Private::TypeInfo<I>; \
|
||||
static_assert(NativeInterface::template isCompatibleWith<T>, \
|
||||
"T::nativeInterface<I>() requires that native interface I is compatible with T"); \
|
||||
\
|
||||
return static_cast<I*>(QNativeInterface::Private::resolveInterface(this, \
|
||||
NativeInterface::name(), NativeInterface::revision())); \
|
||||
return static_cast<NativeInterface*>( \
|
||||
QNativeInterface::Private::resolveInterface(this, \
|
||||
TypeInfo::name(), TypeInfo::revision())); \
|
||||
}
|
||||
|
||||
// Provides a definition for the interface destructor
|
||||
@ -134,6 +206,8 @@ namespace QNativeInterface::Private {
|
||||
revision, TypeInfo<NativeInterface>::revision(), name); \
|
||||
return nullptr; \
|
||||
} \
|
||||
} else { \
|
||||
qCDebug(lcNativeInterface, "No match for requested interface name %s", name); \
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -164,7 +164,7 @@ public:
|
||||
const char * disambiguation = nullptr,
|
||||
int n = -1);
|
||||
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QCoreApplication)
|
||||
|
||||
#ifndef QT_NO_QOBJECT
|
||||
#if QT_CONFIG(future)
|
||||
|
@ -173,7 +173,7 @@ public:
|
||||
bool isSavingSession() const;
|
||||
#endif
|
||||
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QGuiApplication)
|
||||
|
||||
static void sync();
|
||||
Q_SIGNALS:
|
||||
|
@ -73,7 +73,7 @@ public:
|
||||
static void changeKeyboard();
|
||||
static QList<int> possibleKeys(QKeyEvent *e);
|
||||
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QKeyMapper)
|
||||
|
||||
private:
|
||||
friend QKeyMapperPrivate *qt_keymapper_private();
|
||||
|
@ -80,7 +80,7 @@ public:
|
||||
|
||||
QPlatformOffscreenSurface *handle() const;
|
||||
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QOffscreenSurface)
|
||||
|
||||
Q_SIGNALS:
|
||||
void screenChanged(QScreen *screen);
|
||||
|
@ -154,7 +154,7 @@ public:
|
||||
static bool supportsThreadedOpenGL();
|
||||
static QOpenGLContext *globalShareContext();
|
||||
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QOpenGLContext)
|
||||
|
||||
Q_SIGNALS:
|
||||
void aboutToBeDestroyed();
|
||||
|
@ -153,7 +153,7 @@ public:
|
||||
|
||||
qreal refreshRate() const;
|
||||
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QScreen)
|
||||
|
||||
Q_SIGNALS:
|
||||
void geometryChanged(const QRect &geometry);
|
||||
|
@ -289,7 +289,7 @@ public:
|
||||
QVulkanInstance *vulkanInstance() const;
|
||||
#endif
|
||||
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QWindow)
|
||||
|
||||
public Q_SLOTS:
|
||||
Q_REVISION(2, 1) void requestActivate();
|
||||
|
@ -154,7 +154,7 @@ public:
|
||||
static Qt::NavigationMode navigationMode();
|
||||
#endif
|
||||
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QApplication)
|
||||
|
||||
Q_SIGNALS:
|
||||
void focusChanged(QWidget *old, QWidget *now);
|
||||
|
@ -44,7 +44,7 @@ struct InterfaceImplementation;
|
||||
struct PublicClass
|
||||
{
|
||||
PublicClass();
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR
|
||||
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(PublicClass)
|
||||
std::unique_ptr<InterfaceImplementation> m_implementation;
|
||||
};
|
||||
|
||||
@ -66,6 +66,15 @@ QT_DEFINE_NATIVE_INTERFACE(Interface);
|
||||
QT_DEFINE_NATIVE_INTERFACE(OtherInterface);
|
||||
QT_END_NAMESPACE
|
||||
|
||||
struct NotInterface {};
|
||||
|
||||
struct AlmostInterface
|
||||
{
|
||||
struct TypeInfo {
|
||||
// Missing required members
|
||||
};
|
||||
};
|
||||
|
||||
using namespace QNativeInterface;
|
||||
|
||||
struct InterfaceImplementation : public Interface
|
||||
@ -89,6 +98,10 @@ void tst_QNativeInterface::typeInfo() const
|
||||
{
|
||||
using namespace QNativeInterface::Private;
|
||||
|
||||
QCOMPARE(TypeInfo<Interface>::haveTypeInfo, true);
|
||||
QCOMPARE(TypeInfo<NotInterface>::haveTypeInfo, false);
|
||||
QCOMPARE(TypeInfo<AlmostInterface>::haveTypeInfo, false);
|
||||
|
||||
QCOMPARE(TypeInfo<Interface>::isCompatibleWith<PublicClass>, true);
|
||||
QCOMPARE(TypeInfo<Interface>::isCompatibleWith<QObject>, false);
|
||||
QCOMPARE(TypeInfo<Interface>::isCompatibleWith<int>, false);
|
||||
|
Loading…
x
Reference in New Issue
Block a user