diff options
author | Miguel Costa <miguel.costa@qt.io> | 2023-05-17 17:57:01 +0200 |
---|---|---|
committer | Miguel Costa <miguel.costa@qt.io> | 2023-06-12 11:03:35 +0000 |
commit | 8e3a43c0db50774b7b8b661f6a812e37e2408ab3 (patch) | |
tree | 0d9f9b8e2fd0c333d06f8f228ade4f0f37ec2ec6 | |
parent | 36f17aa80578620338705905b5a73166faf92307 (diff) |
Add wrapper facilities for .NET types
Auxiliary types for writing C++ wrapper classes to encapsulate the
low-level interoperability with managed types.
In particular, the QDotNetObject C++ class can be extended to implement
unmanaged proxies for .NET types, allowing native code to seamlessly
interact through a C++ interface with managed code.
Extending both QDotNetObject and QObject in the same wrapper class is
the basis for a close, seamless integration between Qt applications
and .NET code.
Change-Id: Ib6d9472ff113868551ead1375764db7dd79643ea
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
-rw-r--r-- | include/qdotnetarray.h | 147 | ||||
-rw-r--r-- | include/qdotnetcallback.h | 122 | ||||
-rw-r--r-- | include/qdotnetevent.h | 32 | ||||
-rw-r--r-- | include/qdotnetexception.h | 132 | ||||
-rw-r--r-- | include/qdotnetfunction.h | 20 | ||||
-rw-r--r-- | include/qdotnetinterface.h | 85 | ||||
-rw-r--r-- | include/qdotnetmarshal.h | 65 | ||||
-rw-r--r-- | include/qdotnetobject.h | 321 | ||||
-rw-r--r-- | include/qdotnetsafemethod.h | 171 | ||||
-rw-r--r-- | include/qdotnettype.h | 201 |
10 files changed, 1296 insertions, 0 deletions
diff --git a/include/qdotnetarray.h b/include/qdotnetarray.h new file mode 100644 index 0000000..3a0289b --- /dev/null +++ b/include/qdotnetarray.h @@ -0,0 +1,147 @@ +/*************************************************************************************************** + Copyright (C) 2023 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 +***************************************************************************************************/ + +#pragma once + +#include "qdotnetobject.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QString> +#include <QRegularExpression> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +template <typename T, std::enable_if_t< + std::is_fundamental_v<T> + || std::is_same_v<T, QString> + || std::is_base_of_v<QDotNetRef, T>, bool> = true> +class QDotNetArray : public QDotNetObject +{ + class Element; + static QString arrayOf(const QString& typeName) + { + const auto idx = typeName.indexOf(QRegularExpression(",|$")); + return QString("%1[]%2").arg(typeName.left(idx)).arg(typeName.mid(idx)); + } + +public: + Q_DOTNET_OBJECT_INLINE(QDotNetArray, arrayOf(QDotNetTypeOf<T>::TypeName)); + + QDotNetArray(qint32 length) + { + const QString elementTypeName = QDotNetTypeOf<T>::TypeName; + const QDotNetType elementType = QDotNetType::find(elementTypeName); + + QDotNetType arrayType = QDotNetType::find(QDotNetArray::FullyQualifiedTypeName); + auto ctor = constructor<QDotNetArray, qint32>(); + *this = ctor(length); + } + + qint32 length() const + { + return method("get_Length", fnLength).invoke(*this); + } + + T get(qint32 idx) const + { + if constexpr (std::is_fundamental_v<T>) + return method("Get", fnGetValue).invoke(*this, idx); + if constexpr (std::is_same_v<T, QString>) + return method("Get", fnGetObject).invoke(*this, idx).toString(); + if constexpr (std::is_base_of_v<QDotNetRef, T>) + return method("Get", fnGetObject).invoke(*this, idx).template cast<T>(); + throw std::invalid_argument("T"); + } + + void set(qint32 idx, const T &value) + { + if constexpr (std::is_same_v<T, QString>) { + if (!fnSetString.isValid()) { + QDotNetFunction<void, qint32, QDotNetObject> const func = adapter() + .resolveInstanceMethod(*this, "Set", + { UnmanagedType::Void, UnmanagedType::I4, QDotNetParameter::String }); + fnSetString = func; + } + return method("Set", fnSet).invoke(*this, idx, value); + } + return method("Set", fnSet).invoke(*this, idx, value); + } + + Element operator[](qint32 idx) + { + return Element(this, idx); + } + + Element begin() + { + return Element(this, 0); + } + + Element end() + { + return Element(this, length()); + } + +private: + class Element + { + friend class QDotNetArray; + + public: + operator T() + { + return value = a->get(idx); + } + T *operator->() + { + value = a->get(idx); + return &value; + } + T &operator*() + { + value = a->get(idx); + return value; + } + Element &operator=(const T &value) + { + a->set(idx, value); + return *this; + } + bool isEnd() const + { + return idx >= a->length(); + } + Element &operator++() + { + ++idx; + return *this; + } + bool operator !=(const Element &that) const + { + if (isEnd() && that.isEnd()) + return false; + return a != that.a || idx != that.idx; + } + + private: + Element(QDotNetArray *a, qint32 idx) + : a(a) + , idx(idx) + {} + QDotNetArray* a = nullptr; + qint32 idx; + T value; + }; + + mutable QDotNetSafeMethod<qint32> fnLength; + mutable QDotNetSafeMethod<T, qint32> fnGetValue; + mutable QDotNetSafeMethod<QDotNetObject, qint32> fnGetObject; + QDotNetSafeMethod<void, qint32, T> fnSet; + QDotNetSafeMethod<void, qint32, QDotNetObject> fnSetString; +}; diff --git a/include/qdotnetcallback.h b/include/qdotnetcallback.h new file mode 100644 index 0000000..6efea1e --- /dev/null +++ b/include/qdotnetcallback.h @@ -0,0 +1,122 @@ +/*************************************************************************************************** + Copyright (C) 2023 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 +***************************************************************************************************/ + +#pragma once + +#include "qdotnetfunction.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QMap> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#include <functional> + +class QDotNetCallbackBase +{ +protected: + QDotNetCallbackBase() = default; +public: + virtual ~QDotNetCallbackBase() = default; +}; + +template<typename TResult, typename... TArg> +class QDotNetCallback : public QDotNetCallbackBase +{ +public: + using ReturnType = typename QDotNetInbound<TResult>::TargetType; + using FunctionType = std::function<ReturnType( + typename QDotNetInbound<TArg>::TargetType... arg)>; + + using OutboundType = typename QDotNetOutbound<TResult>::OutboundType; + using Delegate = OutboundType(QDOTNETFUNCTION_CALLTYPE *)( + QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType...); + + using CleanUp = void(QDOTNETFUNCTION_CALLTYPE *)(QDotNetCallback *callback, quint64 key); + + QDotNetCallback(FunctionType function) + : function(function) + {} + + ~QDotNetCallback() override = default; + + static Delegate delegate() + { + return callbackDelegate; + } + + static CleanUp cleanUp() + { + return callbackCleanUp; + } + +private: + struct Box + { + ReturnType returnValue; + }; + QMap<quint64, Box *> boxes; + + static OutboundType QDOTNETFUNCTION_CALLTYPE callbackDelegate( + QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType... arg) + { + Box *box = callback->boxes[key] = new Box + { + callback->function(QDotNetInbound<TArg>::convert(arg)...) + }; + const auto result = QDotNetOutbound<TResult>::convert(box->returnValue); + return result; + } + + static void QDOTNETFUNCTION_CALLTYPE callbackCleanUp(QDotNetCallback *callback, quint64 key) + { + if (const Box *box = callback->boxes.take(key)) + delete box; + } + + FunctionType function = nullptr; +}; + +template<typename... TArg> +class QDotNetCallback<void, TArg...> : public QDotNetCallbackBase +{ +public: + using FunctionType = std::function<void(typename QDotNetOutbound<TArg>::SourceType... arg)>; + + QDotNetCallback(FunctionType function) + : function(function) + {} + + ~QDotNetCallback() override = default; + + using Delegate = void(QDOTNETFUNCTION_CALLTYPE *)( + QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType...); + static Delegate delegate() + { + return callbackDelegate; + } + + using CleanUp = void(QDOTNETFUNCTION_CALLTYPE *)(QDotNetCallback *callback, quint64 key); + static CleanUp cleanUp() + { + return callbackCleanUp; + } + +private: + static void QDOTNETFUNCTION_CALLTYPE callbackDelegate(QDotNetCallback *callback, quint64 key, + typename QDotNetInbound<TArg>::InboundType... arg) + { + callback->function(QDotNetInbound<TArg>::convert(arg)...); + } + + static void QDOTNETFUNCTION_CALLTYPE callbackCleanUp(QDotNetCallback *callback, quint64 key) + {} + + FunctionType function = nullptr; +}; diff --git a/include/qdotnetevent.h b/include/qdotnetevent.h new file mode 100644 index 0000000..11df0a1 --- /dev/null +++ b/include/qdotnetevent.h @@ -0,0 +1,32 @@ +/*************************************************************************************************** + Copyright (C) 2023 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 +***************************************************************************************************/ + +#pragma once + +#include "qdotnetobject.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QList> +#include <QString> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +class QDotNetPropertyEvent : public QDotNetObject +{ +public: + Q_DOTNET_OBJECT_INLINE(QDotNetPropertyEvent, "System.ComponentModel.PropertyChangedEventArgs"); + + QString propertyName() const + { + return method("get_PropertyName", fnPropertyName).invoke(*this); + } + +private: + mutable QDotNetSafeMethod<QString> fnPropertyName; +}; diff --git a/include/qdotnetexception.h b/include/qdotnetexception.h new file mode 100644 index 0000000..563c66a --- /dev/null +++ b/include/qdotnetexception.h @@ -0,0 +1,132 @@ +/*************************************************************************************************** + Copyright (C) 2023 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 +***************************************************************************************************/ + +#pragma once + +#include "qdotnetref.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QException> +#include <QString> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +class QDotNetException : public QException, public QDotNetRef +{ +public: + QDotNetException() + : QDotNetException({ "System.Exception" }) + {} + + QDotNetException(const QString &type) + { + const QDotNetFunction<QDotNetException> ctor = adapter().resolveConstructor({ type }); + *this = ctor(); + } + + QDotNetException(const QString &type, const QString &message) + { + const QDotNetFunction<QDotNetException, QString> ctor = adapter().resolveConstructor( + { type, QDotNetTypeOf<QString>::MarshalAs }); + *this = ctor(message); + } + + QDotNetException(const void *objRef = nullptr) + : QDotNetRef(objRef) + {} + + QDotNetException(const QDotNetException &cpySrc) + : QDotNetRef(adapter().addObjectRef(&cpySrc)) + {} + + QDotNetException &operator =(const QDotNetException &cpySrc) + { + QDotNetRef::operator=(cpySrc); + return *this; + } + + QDotNetException(QDotNetException &&movSrc) noexcept + : QDotNetRef(std::move(movSrc)) + {} + + void raise() const override { throw *this; } + QDotNetException *clone() const override { return new QDotNetException(*this); } + + QDotNetException &operator=(QDotNetException &&movSrc) noexcept + { + QDotNetRef::operator=(std::move(movSrc)); + return *this; + } + + template<typename TResult, typename ...TArg> + QDotNetFunction<TResult, TArg...> method(const QString &methodName, + QDotNetFunction<TResult, TArg...> &func) const + { + if (!func.isValid()) { + const QList<QDotNetParameter> parameters + { + QDotNetInbound<TResult>::Parameter, + QDotNetOutbound<TArg>::Parameter... + }; + func = adapter().resolveInstanceMethod(*this, methodName, parameters); + } + return func; + } + + qint32 hResult() const + { + return method("get_HResult", fnHResult).invoke(*this); + } + + QDotNetException innerException() const + { + return method("get_InnerException", fnInnerException).invoke(*this) + .cast<QDotNetException>(); + } + + QString message() const + { + return method("get_Message", fnMessage).invoke(*this); + } + + QString source() const + { + return method("get_Source", fnSource).invoke(*this); + } + + QString stackTrace() const + { + return method("get_StackTrace", fnStackTrace).invoke(*this); + } + + QDotNetRef type() const + { + return method("GetType", fnGetType).invoke(*this); + } + + QString toString() const + { + return method("ToString", fnToString).invoke(*this); + } + + bool equals(const QDotNetRef &obj) const + { + return method("Equals", fnEquals).invoke(*this, obj); + } + +private: + mutable QDotNetFunction<qint32> fnHResult = nullptr; + mutable QDotNetFunction<QDotNetRef> fnInnerException = nullptr; + mutable QDotNetFunction<QString> fnMessage = nullptr; + mutable QDotNetFunction<QString> fnSource = nullptr; + mutable QDotNetFunction<QString> fnStackTrace = nullptr; + mutable QDotNetFunction<QDotNetRef> fnGetType = nullptr; + mutable QDotNetFunction<QString> fnToString = nullptr; + mutable QDotNetFunction<bool, QDotNetRef> fnEquals = nullptr; +}; diff --git a/include/qdotnetfunction.h b/include/qdotnetfunction.h index 531a795..b2b2168 100644 --- a/include/qdotnetfunction.h +++ b/include/qdotnetfunction.h @@ -42,6 +42,17 @@ public: return QDotNetInbound<T>::convert(funcPtr(QDotNetOutbound<TArg>::convert(arg)...)); } + typename QDotNetInbound<T>::TargetType invoke(const QDotNetRef &obj, + typename QDotNetOutbound<TArg>::SourceType... arg) const + { + return operator()(arg...); + } + typename QDotNetInbound<T>::TargetType invoke(nullptr_t nullObj, + typename QDotNetOutbound<TArg>::SourceType... arg) const + { + return operator()(arg...); + } + private: using Delegate = typename QDotNetInbound<T>::InboundType(QDOTNETFUNCTION_CALLTYPE *)( typename QDotNetOutbound<TArg>::OutboundType...); @@ -65,6 +76,15 @@ public: funcPtr(QDotNetOutbound<TArg>::convert(arg)...); } + void invoke(const QDotNetRef &obj, typename QDotNetOutbound<TArg>::SourceType... arg) const + { + operator()(arg...); + } + void invoke(nullptr_t nullObj, typename QDotNetOutbound<TArg>::SourceType... arg) const + { + operator()(arg...); + } + private: using Delegate = void(QDOTNETFUNCTION_CALLTYPE *)( typename QDotNetOutbound<TArg>::OutboundType...); diff --git a/include/qdotnetinterface.h b/include/qdotnetinterface.h new file mode 100644 index 0000000..4dae61c --- /dev/null +++ b/include/qdotnetinterface.h @@ -0,0 +1,85 @@ +/*************************************************************************************************** + Copyright (C) 2023 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 +***************************************************************************************************/ + +#pragma once + +#include "qdotnetref.h" +#include "qdotnetcallback.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QList> +#include <QString> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +class QDotNetInterface : public QDotNetRef +{ +public: + QDotNetInterface(const QString &interfaceName) + : QDotNetRef(adapter().addInterfaceProxy(interfaceName)) + {} + + template<typename TResult, typename... TArg> + void setCallback(const QString &methodName, const QList<QDotNetParameter> ¶ms, + typename QDotNetCallback<TResult, TArg...>::FunctionType function) + { + auto *callback = new QDotNetCallback<TResult, TArg...>(function); + callbacks.append(callback); + + QList<QDotNetParameter> modifiedParams + { + params[0], + UnmanagedType::SysInt, + UnmanagedType::U8 + }; + for (qsizetype i = 1; i < params.size(); ++i) + modifiedParams.append(params[i]); + + adapter().setInterfaceMethod(*this, methodName, modifiedParams, + reinterpret_cast<void *>(callback->delegate()), + reinterpret_cast<void *>(callback->cleanUp()), callback); + } + + template<typename TResult, typename... TArg> + void setCallback(const QString &methodName, + typename QDotNetCallback<TResult, TArg...>::FunctionType function) + { + auto *callback = new QDotNetCallback<TResult, TArg...>(function); + callbacks.append(callback); + + const QList<QDotNetParameter> parameters + { + QDotNetInbound<TResult>::Parameter, + UnmanagedType::SysInt, + UnmanagedType::U8, + QDotNetInbound<TArg>::Parameter... + }; + + adapter().setInterfaceMethod( + *this, methodName, parameters, + callback->delegate(), callback->cleanUp(), callback); + } + + ~QDotNetInterface() override + { + for (const QDotNetCallbackBase *callback : callbacks) + delete callback; + callbacks.clear(); + } + +private: + QList<QDotNetCallbackBase *> callbacks; +}; + +template<typename T> +struct QDotNetTypeOf<T, std::enable_if_t<std::is_base_of_v<QDotNetInterface, T>>> +{ + static inline const QString TypeName = T::FullyQualifiedTypeName; + static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef; +}; diff --git a/include/qdotnetmarshal.h b/include/qdotnetmarshal.h index 0e98b29..a8d27dd 100644 --- a/include/qdotnetmarshal.h +++ b/include/qdotnetmarshal.h @@ -241,3 +241,68 @@ struct QDotNetOutbound<QList<T>, void> return srvValue.constData(); } }; + +template<> +struct QDotNetTypeOf<QDotNetRef> +{ + static inline const QString TypeName = QStringLiteral("System.Object"); + static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef; +}; + +template<> +struct QDotNetTypeOf<QDotNetType> +{ + static inline const QString TypeName = QStringLiteral("System.Type"); + static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef; +}; + +template<> +struct QDotNetTypeOf<QDotNetException> +{ + static inline const QString TypeName = QStringLiteral("System.Exception"); + static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef; +}; + +template<typename T> +struct QDotNetOutbound<T, std::enable_if_t<std::is_base_of_v<QDotNetRef, T>>> +{ + using SourceType = const T&; + using OutboundType = const void*; + static inline const QDotNetParameter Parameter = + QDotNetParameter(QDotNetTypeOf<T>::TypeName, QDotNetTypeOf<T>::MarshalAs); + static OutboundType convert(SourceType dotNetObj) + { + return dotNetObj.gcHandle(); + } +}; + +template<typename T> +struct QDotNetInbound<T, std::enable_if_t<std::is_base_of_v<QDotNetRef, T>>> +{ + using InboundType = const void*; + using TargetType = T; + static inline const QDotNetParameter Parameter = + QDotNetParameter(QDotNetTypeOf<T>::TypeName, QDotNetTypeOf<T>::MarshalAs); + static TargetType convert(InboundType gcHandle) + { + return T(gcHandle); + } +}; + +template<typename T> +struct QDotNetNull<T, std::enable_if_t<std::is_base_of_v<QDotNetRef, T>>> +{ + static T value() { return T(nullptr); } + static bool isNull(const T &obj) { return !obj.isValid(); } +}; + +namespace QtDotNet +{ + using Null = QDotNetNull<QDotNetRef>; + + template<typename T> + bool isNull(const T &obj) { return QDotNetNull<T>::isNull(obj); } + + template<typename T> + T null() { return QDotNetNull<T>::value(); } +} diff --git a/include/qdotnetobject.h b/include/qdotnetobject.h new file mode 100644 index 0000000..34dbc3c --- /dev/null +++ b/include/qdotnetobject.h @@ -0,0 +1,321 @@ +/*************************************************************************************************** + Copyright (C) 2023 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 +***************************************************************************************************/ + +#pragma once + +#include "qdotnettype.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QList> +#include <QString> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +class QDotNetObject : public QDotNetRef +{ +private: + // nullptr and void* ctors +#define Q_DOTNET_OBJECT_REF(T)\ + T(nullptr_t);\ + T(const void *) + + // Copy ctor and assignment +#define Q_DOTNET_OBJECT_COPY(T)\ + T(const T &);\ + T &operator =(const T &) + + // Move ctor and assignment +#define Q_DOTNET_OBJECT_MOVE(T)\ + T(T &&) noexcept;\ + T &operator=(T &&) noexcept + + // Fully qualified .NET class name +#define Q_DOTNET_OBJECT_TYPE(T,type_name)\ + static inline const QString &FullyQualifiedTypeName = QString(type_name) + + // All required declarations +#define Q_DOTNET_OBJECT(T,type_name)\ + Q_DOTNET_OBJECT_REF(T);\ + Q_DOTNET_OBJECT_COPY(T);\ + Q_DOTNET_OBJECT_MOVE(T);\ + Q_DOTNET_OBJECT_TYPE(T, type_name) + + // nullptr and void* ctors +#define Q_DOTNET_OBJECT_REF_IMPL(T,...)\ + T::T(nullptr_t) \ + : QDotNetObject(nullptr) __VA_ARGS__ \ + {}\ + T::T(const void *objectRef) \ + : QDotNetObject(objectRef) __VA_ARGS__ \ + {} + + // Copy ctor and assignment +#define Q_DOTNET_OBJECT_COPY_IMPL(T,...)\ + T::T(const T &cpySrc) \ + : QDotNetObject(cpySrc) __VA_ARGS__ \ + {}\ + T &T::operator=(const T &cpySrc)\ + {\ + QDotNetObject::operator=(cpySrc);\ + return *this;\ + } + + // Move ctor and assignment +#define Q_DOTNET_OBJECT_MOVE_IMPL(T,...)\ + T::T(T &&movSrc) noexcept \ + : QDotNetObject(std::move(movSrc)) __VA_ARGS__\ + {}\ + T &T::operator=(T &&movSrc) noexcept\ + {\ + QDotNetObject::operator=(std::move(movSrc));\ + return *this;\ + } + + // All required ctor and assignment implementations +#define Q_DOTNET_OBJECT_IMPL(T,...)\ + Q_DOTNET_OBJECT_REF_IMPL(T, __VA_ARGS__)\ + Q_DOTNET_OBJECT_COPY_IMPL(T, __VA_ARGS__)\ + Q_DOTNET_OBJECT_MOVE_IMPL(T, __VA_ARGS__) + + // nullptr and void* ctors +#define Q_DOTNET_OBJECT_REF_INLINE(T,...)\ + T(nullptr_t)\ + : QDotNetObject(nullptr) __VA_ARGS__\ + {}\ + T(const void *objectRef)\ + : QDotNetObject(objectRef) __VA_ARGS__\ + {} + + // Copy ctor and assignment +#define Q_DOTNET_OBJECT_COPY_INLINE(T,...)\ + T(const T &cpySrc)\ + : QDotNetObject(cpySrc) __VA_ARGS__\ + {}\ + T &operator=(const T &cpySrc)\ + {\ + QDotNetObject::operator=(cpySrc);\ + return *this;\ + } + + // Move ctor and assignment +#define Q_DOTNET_OBJECT_MOVE_INLINE(T,...)\ + T(T &&movSrc) noexcept\ + : QDotNetObject(std::move(movSrc)) __VA_ARGS__\ + {}\ + T &operator=(T &&movSrc) noexcept\ + {\ + QDotNetObject::operator=(std::move(movSrc));\ + return *this;\ + } + + // All required ctor and assignment implementations +#define Q_DOTNET_OBJECT_INLINE(T,type_name,...)\ + Q_DOTNET_OBJECT_REF_INLINE(T, __VA_ARGS__)\ + Q_DOTNET_OBJECT_COPY_INLINE(T, __VA_ARGS__)\ + Q_DOTNET_OBJECT_MOVE_INLINE(T, __VA_ARGS__)\ + Q_DOTNET_OBJECT_TYPE(T, type_name) + + // Member initialization list for derived class +#define Q_DOTNET_OBJECT_INIT(...) , __VA_ARGS__ + +public: + static inline const QString &FullyQualifiedTypeName = QStringLiteral("System.Object"); + + QDotNetObject(const void *objectRef = nullptr) + : QDotNetRef(objectRef) + {} + + QDotNetObject(const QDotNetObject &cpySrc) + : QDotNetRef(cpySrc) + {} + + QDotNetObject &operator =(const QDotNetObject &cpySrc) + { + QDotNetRef::operator=(cpySrc); + return *this; + } + + QDotNetObject(QDotNetObject &&movSrc) noexcept + : QDotNetRef(std::move(movSrc)) + {} + + QDotNetObject &operator=(QDotNetObject &&movSrc) noexcept + { + QDotNetRef::operator=(std::move(movSrc)); + return *this; + } + + const QDotNetType &type() const + { + if (!fnGetType.isValid()) { + method("GetType", fnGetType); + objType = fnGetType.invoke(*this); + } + return objType; + } + + QString toString() const + { + return method("ToString", fnToString).invoke(*this); + } + + bool equals(const QDotNetRef &obj) const + { + return method("Equals", fnEquals).invoke(*this, obj); + } + + template<typename TResult, typename ...TArg> + QDotNetFunction<TResult, TArg...> method(const QString &methodName) const + { + const QList<QDotNetParameter> parameters + { + QDotNetInbound<TResult>::Parameter, + QDotNetOutbound<TArg>::Parameter... + }; + return adapter().resolveInstanceMethod(*this, methodName, parameters); + } + + template<typename TResult, typename ...TArg> + QDotNetFunction<TResult, TArg...> &method(const QString &methodName, + QDotNetFunction<TResult, TArg...> &func) const + { + if (!func.isValid()) + func = method<TResult, TArg...>(methodName); + return func; + } + + template<typename TResult, typename ...TArg> + QDotNetSafeMethod<TResult, TArg...> &method(const QString &methodName, + QDotNetSafeMethod<TResult, TArg...> &func) const + { + if (!func.isValid()) + func = method<TResult, TArg...>(methodName); + return func; + } + + template<typename TResult, typename ...TArg> + QDotNetFunction<TResult, TArg...> staticMethod(const QString &methodName) const + { + return type().staticMethod<TResult, TArg...>(methodName); + } + + template<typename TResult, typename ...TArg> + QDotNetFunction<TResult, TArg...> &staticMethod(const QString &methodName, + QDotNetFunction<TResult, TArg...> &func) const + { + if (!func.isValid()) + func = type().staticMethod<TResult, TArg...>(methodName); + return func; + } + + template<typename TResult, typename ...TArg> + QDotNetSafeMethod<TResult, TArg...> &staticMethod(const QString &methodName, + QDotNetSafeMethod<TResult, TArg...> &func) const + { + if (!func.isValid()) + func = type().staticMethod<TResult, TArg...>(methodName); + return func; + } + + template<typename ...TArg> + static QDotNetFunction<QDotNetObject, TArg...> constructor(const QString &typeName) + { + return QDotNetType::constructor<QDotNetObject, TArg...>(typeName); + } + + template<typename ...TArg> + static QDotNetFunction<QDotNetObject, TArg...> &constructor(const QString &typeName, + QDotNetFunction<QDotNetObject, TArg...> &ctor) + { + return QDotNetType::constructor(typeName, ctor); + } + + template<typename ...TArg> + static QDotNetSafeMethod<QDotNetObject, TArg...> &constructor(const QString &typeName, + QDotNetSafeMethod<QDotNetObject, TArg...> &ctor) + { + return QDotNetType::constructor(typeName, ctor); + } + + struct IEventHandler + { + virtual ~IEventHandler() = default; + virtual void handleEvent(const QString &eventName, QDotNetObject &eventSource, + QDotNetObject &eventArgs) = 0; + }; + + void subscribeEvent(const QString &eventName, IEventHandler *eventHandler) + { + adapter().addEventHandler(*this, eventName, eventHandler, eventCallback); + } + + void unsubscribeEvent(const QString &eventName, IEventHandler *eventHandler) + { + adapter().removeEventHandler(*this, eventName, eventHandler); + } + + QDotNetObject object(const QString &path) + { + return adapter().object(*this, path); + } + + template<typename TResult, typename ...TArg> + TResult call(const QString &methodName, TArg... arg) const + { + return method<TResult, TArg...>(methodName).invoke(*this, arg...); + } + + template<typename ...TArg> + void call(const QString &methodName, TArg... arg) const + { + method<void, TArg...>(methodName).invoke(*this, arg...); + } + +protected: + template<typename T, typename ...TArg> + static QDotNetFunction<T, TArg...> constructor() + { + return QDotNetType::constructor<T, TArg...>(T::FullyQualifiedTypeName); + } + + template<typename T, typename ...TArg> + static QDotNetFunction<T, TArg...> &constructor(QDotNetFunction<T, TArg...> &ctor) + { + return QDotNetType::constructor(T::FullyQualifiedTypeName, ctor); + } + + template<typename T, typename ...TArg> + static QDotNetSafeMethod<T, TArg...> &constructor(QDotNetSafeMethod<T, TArg...> &ctor) + { + return QDotNetType::constructor(T::FullyQualifiedTypeName, ctor); + } + +private: + static void QDOTNETFUNCTION_CALLTYPE eventCallback(void *context, void *eventNameChars, + void *eventSourceRef, void *eventArgsRef) + { + auto *receiver = static_cast<IEventHandler *>(context); + const QString eventName(static_cast<const QChar *>(eventNameChars)); + QDotNetObject eventSource(eventSourceRef); + QDotNetObject eventArgs(eventArgsRef); + receiver->handleEvent(eventName, eventSource, eventArgs); + } + + mutable QDotNetFunction<QDotNetType> fnGetType; + mutable QDotNetType objType = nullptr; + mutable QDotNetFunction<QString> fnToString; + mutable QDotNetFunction<bool, QDotNetRef> fnEquals; +}; + +template<typename T> +struct QDotNetTypeOf<T, std::enable_if_t<std::is_base_of_v<QDotNetObject, T>>> +{ + static inline const QString TypeName = T::FullyQualifiedTypeName; + static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef; +}; diff --git a/include/qdotnetsafemethod.h b/include/qdotnetsafemethod.h new file mode 100644 index 0000000..a67b708 --- /dev/null +++ b/include/qdotnetsafemethod.h @@ -0,0 +1,171 @@ +/*************************************************************************************************** + Copyright (C) 2023 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 +***************************************************************************************************/ + +#pragma once + +#include "qdotnetexception.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QList> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +template<typename T, typename... TArg> +class QDotNetSafeMethod +{ + using FuncType = QDotNetFunction<T, TArg...>; + using SafeFuncType = QDotNetFunction<QDotNetRef, QDotNetRef, TArg...>; + +public: + QDotNetSafeMethod() = default; + QDotNetSafeMethod(FuncType func) + : func(func) + { + const QList<QDotNetParameter> parameters + { + QDotNetInbound<QDotNetRef>::Parameter, + QDotNetOutbound<QDotNetRef>::Parameter, + QDotNetOutbound<TArg>::Parameter... + }; + safeFunc = QDotNetAdapter::instance().resolveSafeMethod(func.ptr(), parameters); + } + + bool isValid() const { return func.isValid(); } + + class SafeReturn : public QDotNetRef + { + public: + SafeReturn(const void *objectRef = nullptr) + : QDotNetRef(objectRef) + {} + + SafeReturn(const SafeReturn &cpySrc) + : QDotNetRef(cpySrc) + {} + + SafeReturn(SafeReturn &&movSrc) noexcept + : QDotNetRef(std::move(movSrc)) + {} + + T value() const + { + if (!fnValue.isValid()) { + fnValue = adapter().resolveInstanceMethod( + *this, "get_Value", { QDotNetInbound<T>::Parameter }); + } + return fnValue(); + } + QDotNetException exception() const + { + if (!fnException.isValid()) { + fnException = adapter().resolveInstanceMethod( + *this, "get_Exception", { QDotNetInbound<QDotNetException>::Parameter }); + } + return fnException(); + } + private: + mutable QDotNetFunction<T> fnValue = nullptr; + mutable QDotNetFunction<QDotNetException> fnException = nullptr; + }; + + typename QDotNetInbound<T>::TargetType invoke(QDotNetOutbound<QDotNetRef>::SourceType obj, + typename QDotNetOutbound<TArg>::SourceType... arg) const + { + SafeReturn safeReturn = safeFunc(obj, arg...).template cast<SafeReturn>(); + QDotNetException exception = safeReturn.exception(); + if (!exception.isValid()) + return safeReturn.value(); + throw exception; + } + + typename QDotNetInbound<T>::TargetType invoke(nullptr_t nullObj, + typename QDotNetOutbound<TArg>::SourceType... arg) const + { + SafeReturn safeReturn = safeFunc(nullObj, arg...).template cast<SafeReturn>(); + QDotNetException exception = safeReturn.exception(); + if (!exception.isValid()) + return safeReturn.value(); + throw exception; + } + +private: + FuncType func = nullptr; + SafeFuncType safeFunc = nullptr; +}; + +template<typename... TArg> +class QDotNetSafeMethod<void, TArg...> +{ + using FuncType = QDotNetFunction<void, TArg...>; + using SafeFuncType = QDotNetFunction<QDotNetRef, QDotNetRef, TArg...>; + +public: + QDotNetSafeMethod() = default; + QDotNetSafeMethod(FuncType func) + : func(func) + { + const QList<QDotNetParameter> parameters + { + QDotNetInbound<QDotNetRef>::Parameter, + QDotNetOutbound<QDotNetRef>::Parameter, + QDotNetOutbound<TArg>::Parameter... + }; + safeFunc = QDotNetAdapter::instance().resolveSafeMethod(func.ptr(), parameters); + } + + bool isValid() const { return func.isValid(); } + + class SafeReturn : public QDotNetRef + { + public: + SafeReturn(const void *objectRef = nullptr) + : QDotNetRef(objectRef) + {} + + SafeReturn(const SafeReturn &cpySrc) + : QDotNetRef(cpySrc) + {} + + SafeReturn(SafeReturn &&movSrc) noexcept + : QDotNetRef(std::move(movSrc)) + {} + + QDotNetException exception() const + { + if (!fnException.isValid()) { + fnException = QDotNetAdapter::instance().resolveInstanceMethod( + *this, "get_Exception", { QDotNetInbound<QDotNetException>::Parameter }); + } + return fnException(); + } + private: + mutable QDotNetFunction<QDotNetException> fnException = nullptr; + }; + + void invoke(QDotNetOutbound<QDotNetRef>::SourceType obj, + typename QDotNetOutbound<TArg>::SourceType... arg) const + { + SafeReturn safeReturn = safeFunc(obj, arg...).template cast<SafeReturn>(); + QDotNetException exception = safeReturn.exception(); + if (exception.isValid()) + throw exception; + } + + void invoke(nullptr_t nullObj, typename QDotNetOutbound<TArg>::SourceType... arg) const + { + SafeReturn safeReturn = safeFunc(nullObj, arg...).template cast<SafeReturn>(); + QDotNetException exception = safeReturn.exception(); + if (exception.isValid()) + throw exception; + } + +private: + FuncType func = nullptr; + SafeFuncType safeFunc = nullptr; +}; diff --git a/include/qdotnettype.h b/include/qdotnettype.h new file mode 100644 index 0000000..4ab99bf --- /dev/null +++ b/include/qdotnettype.h @@ -0,0 +1,201 @@ +/*************************************************************************************************** + Copyright (C) 2023 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 +***************************************************************************************************/ + +#pragma once + +#include "qdotnetsafemethod.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QString> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +class QDotNetType : public QDotNetRef +{ +public: + static inline const QString &FullyQualifiedTypeName = QStringLiteral("System.Type"); + + QDotNetType(const void *typeRef = nullptr) + : QDotNetRef(typeRef) + {} + + QDotNetType(const QDotNetType &cpySrc) + : QDotNetRef(adapter().addObjectRef(&cpySrc)) + {} + + QDotNetType &operator =(const QDotNetType &cpySrc) + { + QDotNetRef::operator=(cpySrc); + return *this; + } + + QDotNetType(QDotNetType &&movSrc) noexcept + : QDotNetRef(std::move(movSrc)) + {} + + QDotNetType &operator=(QDotNetType &&movSrc) noexcept + { + QDotNetRef::operator=(std::move(movSrc)); + return *this; + } + + QString fullName() const + { + if (!isValid()) + return QStringLiteral(""); + if (!fnFullName.isValid()) { + fnFullName = adapter().resolveInstanceMethod(*this, + "get_FullName", { UnmanagedType::LPWStr }); + strFullName = fnFullName(); + } + return strFullName; + } + + static QDotNetType find(const QString &typeName) + { + QDotNetFunction<QDotNetType, QString> fnGetType; + return staticMethod(FullyQualifiedTypeName, "GetType", fnGetType).invoke(nullptr, typeName); + } + + template<typename T> + static QDotNetType find() + { + return find(T::FullyQualifiedTypeName); + } + + template<typename TResult, typename ...TArg> + static QDotNetFunction<TResult, TArg...> staticMethod(const QString &typeName, + const QString &methodName) + { + const QList<QDotNetParameter> parameters + { + QDotNetInbound<TResult>::Parameter, + QDotNetOutbound<TArg>::Parameter... + }; + return adapter().resolveStaticMethod(typeName, methodName, parameters); + } + + template<typename TResult, typename ...TArg> + static QDotNetFunction<TResult, TArg...> &staticMethod(const QString &typeName, + const QString &methodName, QDotNetFunction<TResult, TArg...> &func) + { + if (!func.isValid()) + func = staticMethod<TResult, TArg...>(typeName, methodName); + return func; + } + + template<typename TResult, typename ...TArg> + static QDotNetSafeMethod<TResult, TArg...> &staticMethod(const QString &typeName, + const QString &methodName, QDotNetSafeMethod<TResult, TArg...> &func) + { + if (!func.isValid()) + func = staticMethod<TResult, TArg...>(typeName, methodName); + return func; + } + + template<typename TResult, typename ...TArg> + QDotNetFunction<TResult, TArg...> staticMethod(const QString &methodName) const + { + return staticMethod<TResult, TArg...>(fullName(), methodName); + } + + template<typename TResult, typename ...TArg> + QDotNetFunction<TResult, TArg...> &staticMethod(const QString &methodName, + QDotNetFunction<TResult, TArg...> &func) const + { + if (!func.isValid()) + func = staticMethod<TResult, TArg...>(methodName); + return func; + } + + template<typename TResult, typename ...TArg> + QDotNetSafeMethod<TResult, TArg...> &staticMethod(const QString &methodName, + QDotNetSafeMethod<TResult, TArg...> &func) const + { + if (!func.isValid()) + func = staticMethod<TResult, TArg...>(methodName); + return func; + } + + template<typename T, typename ...TArg> + static QDotNetFunction<T, TArg...> constructor(const QString &typeName) + { + const QList<QDotNetParameter> parameters + { + QDotNetParameter(typeName, UnmanagedType::ObjectRef), + QDotNetOutbound<TArg>::Parameter... + }; + return adapter().resolveConstructor(parameters); + } + + template<typename T, typename ...TArg> + static QDotNetFunction<T, TArg...> &constructor(const QString &typeName, + QDotNetFunction<T, TArg...> &ctor) + { + if (!ctor.isValid()) + ctor = constructor<T, TArg...>(typeName); + return ctor; + } + + template<typename T, typename ...TArg> + static QDotNetSafeMethod<T, TArg...> &constructor(const QString &typeName, + QDotNetSafeMethod<T, TArg...> &ctor) + { + if (!ctor.isValid()) + ctor = constructor<T, TArg...>(typeName); + return ctor; + } + + template<typename T, typename ...TArg> + QDotNetFunction<T, TArg...> constructor() const + { + return constructor<T, TArg...>(fullName()); + } + + template<typename T, typename ...TArg> + QDotNetFunction<T, TArg...> &constructor(QDotNetFunction<T, TArg...> &ctor) const + { + return constructor(fullName(), ctor); + } + + template<typename T, typename ...TArg> + QDotNetFunction<T, TArg...> &constructor(QDotNetSafeMethod<T, TArg...> &ctor) const + { + return constructor(fullName(), ctor); + } + + void freeTypeRef() + { + freeTypeRef(fullName()); + } + + static void freeTypeRef(const QString &typeName) + { + adapter().freeTypeRef(typeName); + } + + template<typename T> + static void freeTypeRef() + { + freeTypeRef(T::FullyQualifiedTypeName); + } + +private: + mutable QDotNetFunction<QString> fnFullName; + mutable QString strFullName; +}; + +namespace QtDotNet +{ + template<typename T, typename... TArg> + T call(const QString &type, const QString &method, TArg... arg) + { + return QDotNetType::staticMethod<T, TArg...>(type, method).invoke(nullptr, arg...); + } +} |