aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Costa <miguel.costa@qt.io>2023-05-17 17:57:01 +0200
committerMiguel Costa <miguel.costa@qt.io>2023-06-12 11:03:35 +0000
commit8e3a43c0db50774b7b8b661f6a812e37e2408ab3 (patch)
tree0d9f9b8e2fd0c333d06f8f228ade4f0f37ec2ec6
parent36f17aa80578620338705905b5a73166faf92307 (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.h147
-rw-r--r--include/qdotnetcallback.h122
-rw-r--r--include/qdotnetevent.h32
-rw-r--r--include/qdotnetexception.h132
-rw-r--r--include/qdotnetfunction.h20
-rw-r--r--include/qdotnetinterface.h85
-rw-r--r--include/qdotnetmarshal.h65
-rw-r--r--include/qdotnetobject.h321
-rw-r--r--include/qdotnetsafemethod.h171
-rw-r--r--include/qdotnettype.h201
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> &params,
+ 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...);
+ }
+}