diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 14887a1..ccb9561 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -16,6 +16,7 @@ find_package(Qt5Widgets) set(HEADERS_LIST include/DOtherSide/BaseQAbstractListModel.h include/DOtherSide/DOtherSideTypes.h + include/DOtherSide/DOtherSideTypesCpp.h include/DOtherSide/DynamicSignal.h include/DOtherSide/BaseQObject.h include/DOtherSide/DynamicProperty.h @@ -26,6 +27,7 @@ set(HEADERS_LIST include/DOtherSide/OnSlotExecutedHandler.h include/DOtherSide/DynamicQObjectFactory.h include/DOtherSide/DynamicQObject2.h + include/DOtherSide/Utils.h ) set(SRC_LIST @@ -36,6 +38,7 @@ set(SRC_LIST src/DynamicProperty.cpp src/DynamicQObjectFactory.cpp src/DynamicQObject2.cpp + src/DOtherSideTypesCpp.cpp ) include_directories(include include/Qt) diff --git a/lib/include/DOtherSide/DOtherSide.h b/lib/include/DOtherSide/DOtherSide.h index b293e6c..817bbf1 100644 --- a/lib/include/DOtherSide/DOtherSide.h +++ b/lib/include/DOtherSide/DOtherSide.h @@ -169,6 +169,17 @@ DOS_API void dos_qurl_create(void** vptr, const char* url, int parsingMode); DOS_API void dos_qurl_delete(void* vptr); DOS_API void dos_qurl_to_string(void* vptr, char** result); +// QObjectFactory +DOS_API void dos_qobjectfactory_create(void** vptr, + SignalDefinitions signalDefinitions, + SlotDefinitions slotDefinitions, + PropertyDefinitions propertyDefinitions); +DOS_API void dos_qobjectfactory_delete(void* vptr); +DOS_API void dos_qobjectfactory_create_qobject(void* vptr, + void* dObjectPointer, + DObjectCallback dObjectCallback, + void** result); + #ifdef __cplusplus } #endif diff --git a/lib/include/DOtherSide/DOtherSideTypes.h b/lib/include/DOtherSide/DOtherSideTypes.h index 311ba19..402caa5 100644 --- a/lib/include/DOtherSide/DOtherSideTypes.h +++ b/lib/include/DOtherSide/DOtherSideTypes.h @@ -30,8 +30,50 @@ typedef void(*RoleNamesCallback)(void* model, QHashIntQByteArrayVoidPtr result); typedef void(*FlagsCallback) (void* model, QModelIndexVoidPtr index, IntPtr result); typedef void(*HeaderDataCallback) (void* model, int section, int orientation, int role, QVariantVoidPtr result); +struct SignalDefinition +{ + const char* name; + int parametersCount; + int* parametersMetaTypes; +}; + +struct SignalDefinitions +{ + int count; + SignalDefinition* definitions; +}; + +struct SlotDefinition +{ + const char* name; + int returnMetaType; + int parametersCount; + int* parametersMetaTypes; +}; + +struct SlotDefinitions +{ + int count; + SlotDefinition* definitions; +}; + +struct PropertyDefinition +{ + const char* name; + int propertyMetaType; + const char* readSlot; + const char* writeSlot; + const char* notifySignal; +}; + +struct PropertyDefinitions +{ + int count; + PropertyDefinition* definitions; +}; + #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/lib/include/DOtherSide/DOtherSideTypesCpp.h b/lib/include/DOtherSide/DOtherSideTypesCpp.h new file mode 100644 index 0000000..65ea752 --- /dev/null +++ b/lib/include/DOtherSide/DOtherSideTypesCpp.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include + +#include "DOtherSide/DOtherSideTypes.h" +#include "DOtherSide/Utils.h" + +namespace DOS +{ +struct SignalDefinition +{ + SignalDefinition(QString n, + std::vector v) + : name(std::move(n)) + , parameterTypes(std::move(v)) + {} + + SignalDefinition(::SignalDefinition cType) + : name(QString::fromUtf8(cType.name)) + { + auto wrapped = wrap_array(cType.parametersMetaTypes, cType.parametersCount); + auto conversion = [](int type)->QMetaType::Type { return static_cast(type);}; + std::transform(wrapped.begin(), wrapped.end(), parameterTypes.begin(), conversion); + } + + QString name; + std::vector parameterTypes; +}; + +struct SlotDefinition +{ + SlotDefinition(QString n, + QMetaType::Type t, + std::vector v) + : name(std::move(n)) + , returnType(std::move(t)) + , parameterTypes(std::move(v)) + {} + + SlotDefinition(::SlotDefinition cType) + : name(QString::fromUtf8(cType.name)) + , returnType(static_cast(cType.returnMetaType)) + { + auto wrapped = wrap_array(cType.parametersMetaTypes, cType.parametersCount); + auto conversion = [](int type)->QMetaType::Type { return static_cast(type);}; + std::transform(wrapped.begin(), wrapped.end(), parameterTypes.begin(), conversion); + } + + QString name; + QMetaType::Type returnType; + std::vector parameterTypes; +}; + +struct PropertyDefinition +{ + PropertyDefinition(QString n, + QMetaType::Type t, + QString r, + QString w, + QString s) + : name(std::move(n)) + , type(std::move(t)) + , readSlot(std::move(r)) + , writeSlot(std::move(w)) + , notifySignal(std::move(s)) + {} + + PropertyDefinition(::PropertyDefinition cType) + : name(cType.name) + , type(static_cast(cType.propertyMetaType)) + , readSlot(QString::fromUtf8(cType.readSlot)) + , writeSlot(QString::fromUtf8(cType.writeSlot)) + , notifySignal(QString::fromUtf8(cType.notifySignal)) + {} + + QString name; + QMetaType::Type type; + QString readSlot; + QString writeSlot; + QString notifySignal; +}; + +using SignalDefinitions = std::vector; +using SlotDefinitions = std::vector; +using PropertyDefinitions = std::vector; + +SignalDefinitions toVector(const ::SignalDefinitions& cType); +SlotDefinitions toVector(const ::SlotDefinitions& cType); +PropertyDefinitions toVector(const ::PropertyDefinitions& cType); + +} diff --git a/lib/include/DOtherSide/DynamicQObject2.h b/lib/include/DOtherSide/DynamicQObject2.h index 2075c79..c1d0065 100644 --- a/lib/include/DOtherSide/DynamicQObject2.h +++ b/lib/include/DOtherSide/DynamicQObject2.h @@ -3,12 +3,15 @@ #include #include +namespace DOS +{ + class DynamicQObjectFactory; class DynamicQObject2 : public QObject { public: - using OnSlotExecuted = std::function&)>; + using OnSlotExecuted = std::function&)>; DynamicQObject2(const DynamicQObjectFactory* factory, OnSlotExecuted handler); @@ -25,3 +28,5 @@ private: const DynamicQObjectFactory* const m_factory; const OnSlotExecuted m_handler; }; + +} // namespace DOS diff --git a/lib/include/DOtherSide/DynamicQObjectFactory.h b/lib/include/DOtherSide/DynamicQObjectFactory.h index 31efc66..d94939a 100644 --- a/lib/include/DOtherSide/DynamicQObjectFactory.h +++ b/lib/include/DOtherSide/DynamicQObjectFactory.h @@ -3,48 +3,28 @@ #include #include #include + #include #include #include -struct SignalDefinition -{ - QString name; - std::vector argumentsTypes; -}; +#include "DOtherSide/DOtherSideTypesCpp.h" -struct SlotDefinition +namespace DOS { - QString name; - QMetaType::Type returnType; - std::vector argumentsTypes; -}; - -struct PropertyDefinition -{ - QString name; - QMetaType::Type type; - QString readSlot; - QString writeSlot; - QString notifySignal; -}; class DynamicQObject2; class DynamicQObjectFactory { - using SignalDefinitions = std::vector; - using SlotDefinitions = std::vector; - using PropertyDefinitions = std::vector; using SafeQMetaObjectPtr = std::unique_ptr; - using OnSlotExecuted = std::function&)>; + using OnSlotExecuted = std::function&)>; public: DynamicQObjectFactory(SignalDefinitions signalDefinitions, SlotDefinitions slotDefinitions, PropertyDefinitions propertyDefinitions); - DynamicQObject2* create(OnSlotExecuted handler) const; inline const QMetaObject* metaObject() const; inline int signalSlotIndex(const QString& signalName) const; @@ -76,3 +56,5 @@ inline int DynamicQObjectFactory::writeSlotIndex(const char *propertyName) const { return m_propertySlots.value(propertyName, {-1,-1}).second; } + +} // namespace DOS diff --git a/lib/include/DOtherSide/OnSlotExecutedHandler.h b/lib/include/DOtherSide/OnSlotExecutedHandler.h index 9b4eb24..3e2f0af 100644 --- a/lib/include/DOtherSide/OnSlotExecutedHandler.h +++ b/lib/include/DOtherSide/OnSlotExecutedHandler.h @@ -14,7 +14,8 @@ class OnSlotExecutedHandler public: OnSlotExecutedHandler(void* dObjectPointer, IDynamicQObject::Callback dObjectCallback); - QVariant operator()(const DynamicSlot& slot, std::vector const& args); + QVariant operator()(const QString& name, const std::vector& args); + QVariant operator()(const DynamicSlot& slot, const std::vector& args); private: void* m_dObjectPointer; diff --git a/lib/include/DOtherSide/Utils.h b/lib/include/DOtherSide/Utils.h new file mode 100644 index 0000000..517e9b8 --- /dev/null +++ b/lib/include/DOtherSide/Utils.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace DOS +{ + +template +struct wrapped_array { + wrapped_array(T* first, T* last) : begin_ {first}, end_ {last} {} + wrapped_array(T* first, std::ptrdiff_t size) + : wrapped_array {first, first + size} {} + + T* begin() const noexcept { return begin_; } + T* end() const noexcept { return end_; } + + T* begin_; + T* end_; +}; + +template +wrapped_array wrap_array(T* first, std::ptrdiff_t size) noexcept +{ return {first, size}; } + +template +std::vector toVector(const wrapped_array& array) +{ + std::vector result; + std::copy(array.begin(), array.end(), result.begin()); + return result; +} +} diff --git a/lib/src/DOtherSide.cpp b/lib/src/DOtherSide.cpp index 06de134..f0e74d6 100644 --- a/lib/src/DOtherSide.cpp +++ b/lib/src/DOtherSide.cpp @@ -13,10 +13,14 @@ #include #include +#include "DOtherSide/DOtherSideTypesCpp.h" #include "DOtherSide/DynamicQObject.h" #include "DOtherSide/BaseQAbstractListModel.h" #include "DOtherSide/BaseQObject.h" #include "DOtherSide/OnSlotExecutedHandler.h" +#include "DOtherSide/DynamicQObjectFactory.h" +#include "DOtherSide/DynamicQObject2.h" + void convert_to_cstring(const QString& source, char** destination) { @@ -649,3 +653,25 @@ void dos_qurl_to_string(void* vptr, char** result) convert_to_cstring(url->toString(), result); } + +void dos_qobjectfactory_create(void **vptr, + SignalDefinitions signalDefinitions, + SlotDefinitions slotDefinitions, + PropertyDefinitions propertyDefinitions) +{ + *vptr = new DOS::DynamicQObjectFactory(DOS::toVector(signalDefinitions), + DOS::toVector(slotDefinitions), + DOS::toVector(propertyDefinitions)); +} + +void dos_qobjectfactory_delete(void *vptr) +{ + auto factory = reinterpret_cast(vptr); + delete factory; +} + +void dos_qobjectfactory_create_qobject(void *vptr, void* dObjectPointer, DObjectCallback dObjectCallback, void **result) +{ + auto factory = reinterpret_cast(vptr); + *result = factory->create(OnSlotExecutedHandler(dObjectPointer, dObjectCallback)); +} diff --git a/lib/src/DOtherSideTypesCpp.cpp b/lib/src/DOtherSideTypesCpp.cpp new file mode 100644 index 0000000..b1b3b45 --- /dev/null +++ b/lib/src/DOtherSideTypesCpp.cpp @@ -0,0 +1,19 @@ +#include "DOtherSide/DOtherSideTypesCpp.h" + +namespace DOS { + +SignalDefinitions toVector(const ::SignalDefinitions& cType) +{ + return toVector(wrap_array(cType.definitions, cType.count)); +} + +SlotDefinitions toVector(const ::SlotDefinitions& cType) +{ + return toVector(wrap_array(cType.definitions, cType.count)); +} + +PropertyDefinitions toVector(const ::PropertyDefinitions& cType) +{ + return toVector(wrap_array(cType.definitions, cType.count)); +} +} diff --git a/lib/src/DynamicQObject2.cpp b/lib/src/DynamicQObject2.cpp index 088a1cf..031323d 100644 --- a/lib/src/DynamicQObject2.cpp +++ b/lib/src/DynamicQObject2.cpp @@ -3,6 +3,9 @@ #include #include +namespace DOS +{ + DynamicQObject2::DynamicQObject2(const DynamicQObjectFactory *factory, OnSlotExecuted handler) : m_factory(factory) @@ -63,7 +66,7 @@ bool DynamicQObject2::executeSlot(int index, void **args) arguments.emplace_back(std::move(argument)); } - QVariant result = m_handler(index, method.name(), arguments); // Execute method + QVariant result = m_handler(method.name(), arguments); // Execute method if (method.returnType() != QMetaType::Void && result.isValid()) { QMetaType::construct(method.returnType(), args[0], result.constData()); @@ -91,3 +94,5 @@ bool DynamicQObject2::writeProperty(int index, void **args) } return executeSlot(m_factory->writeSlotIndex(property.name()), args); } + +} // namespace DOS diff --git a/lib/src/DynamicQObjectFactory.cpp b/lib/src/DynamicQObjectFactory.cpp index c749fad..9bc66f5 100644 --- a/lib/src/DynamicQObjectFactory.cpp +++ b/lib/src/DynamicQObjectFactory.cpp @@ -6,23 +6,28 @@ namespace { - template - QByteArray createSignature(const T& functionDefinition) - { - QString signature("%1(%2)"); - QString arguments; +template +QByteArray createSignature(const T& functionDefinition) +{ + QString signature("%1(%2)"); + QString arguments; - for (QMetaType::Type type : functionDefinition.argumentsTypes) { - if (type != functionDefinition.argumentsTypes.front()) - arguments += QLatin1Char(','); - arguments += QMetaType::typeName(type); - } - - return signature.arg(functionDefinition.name, arguments).toUtf8(); + for (int type : functionDefinition.parameterTypes) { + if (type != functionDefinition.parameterTypes.front()) + arguments += QLatin1Char(','); + arguments += QMetaType::typeName(type); } + + return signature.arg(functionDefinition.name, arguments).toUtf8(); +} } -DynamicQObjectFactory::DynamicQObjectFactory(DynamicQObjectFactory::SignalDefinitions signalDefinitions, DynamicQObjectFactory::SlotDefinitions slotDefinitions, DynamicQObjectFactory::PropertyDefinitions propertyDefinitions) +namespace DOS +{ + +DynamicQObjectFactory::DynamicQObjectFactory(SignalDefinitions signalDefinitions, + SlotDefinitions slotDefinitions, + PropertyDefinitions propertyDefinitions) : m_metaObject(nullptr, ::free) { QMetaObjectBuilder builder; @@ -34,7 +39,7 @@ DynamicQObjectFactory::DynamicQObjectFactory(DynamicQObjectFactory::SignalDefini for (const SignalDefinition& signal : signalDefinitions) { - QMetaMethodBuilder signalBuilder = builder.addSignal(createSignature(signal)); + QMetaMethodBuilder signalBuilder = builder.addSignal(::createSignature(signal)); signalBuilder.setReturnType(QMetaType::typeName(QMetaType::Void)); signalBuilder.setAccess(QMetaMethod::Public); m_signalIndexByName[signal.name.toUtf8()] = signalBuilder.index(); @@ -43,7 +48,7 @@ DynamicQObjectFactory::DynamicQObjectFactory(DynamicQObjectFactory::SignalDefini QHash methodIndexByName; for (const SlotDefinition& slot : slotDefinitions) { - QMetaMethodBuilder methodBuilder = builder.addSlot(createSignature(slot)); + QMetaMethodBuilder methodBuilder = builder.addSlot(::createSignature(slot)); methodBuilder.setReturnType(QMetaType::typeName(slot.returnType)); methodBuilder.setAttributes(QMetaMethod::Scriptable); methodIndexByName[slot.name] = methodBuilder.index(); @@ -68,3 +73,5 @@ DynamicQObject2* DynamicQObjectFactory::create(OnSlotExecuted handler) const { return new DynamicQObject2(this, std::move(handler)); } + +} // namespace DOS diff --git a/lib/src/OnSlotExecutedHandler.cpp b/lib/src/OnSlotExecutedHandler.cpp index 0213207..0a23ec5 100644 --- a/lib/src/OnSlotExecutedHandler.cpp +++ b/lib/src/OnSlotExecutedHandler.cpp @@ -7,7 +7,7 @@ OnSlotExecutedHandler::OnSlotExecutedHandler(void *dObjectPointer, , m_dObjectCallback(dObjectCallback) {} -QVariant OnSlotExecutedHandler::operator()(const DynamicSlot& slot, const std::vector &args) +QVariant OnSlotExecutedHandler::operator()(const QString &name, const std::vector &args) { QVariant result; @@ -15,7 +15,7 @@ QVariant OnSlotExecutedHandler::operator()(const DynamicSlot& slot, const std::v return result; // prepare slot name - QVariant slotName(slot.name()); + QVariant slotName(name); // prepare void* for the QVariants std::vector argumentsAsVoidPointers; @@ -29,3 +29,8 @@ QVariant OnSlotExecutedHandler::operator()(const DynamicSlot& slot, const std::v return result; } + +QVariant OnSlotExecutedHandler::operator()(const DynamicSlot& slot, const std::vector &args) +{ + return operator ()(slot.name(), args); +} diff --git a/test/test_dynamicqobject.cpp b/test/test_dynamicqobject.cpp index 01b34f1..fe2e906 100644 --- a/test/test_dynamicqobject.cpp +++ b/test/test_dynamicqobject.cpp @@ -100,21 +100,22 @@ private slots: } void benchmarkDynamicQObject2Performance() { + QBENCHMARK { - DynamicQObjectFactory factory {{SignalDefinition{"fooChanged", {QMetaType::Int}}}, - {SlotDefinition{"foo", QMetaType::Int, {}}, SlotDefinition{"setFoo", QMetaType::Void, {QMetaType::Int}}}, - {PropertyDefinition{"foo", QMetaType::Int, "foo", "setFoo", "fooChanged"}}}; + DOS::DynamicQObjectFactory factory {{DOS::SignalDefinition{"fooChanged", {QMetaType::Int}}}, + {DOS::SlotDefinition{"foo", QMetaType::Int, {}}, DOS::SlotDefinition{"setFoo", QMetaType::Void, {QMetaType::Int}}}, + {DOS::PropertyDefinition{"foo", QMetaType::Int, "foo", "setFoo", "fooChanged"}}}; for (int i = 0; i < 1000; ++i) { - std::unique_ptr dynamicQObject(factory.create([](int, const QString&, const std::vector&)-> QVariant{})); + std::unique_ptr dynamicQObject(factory.create([](const QString&, const std::vector&)-> QVariant{})); } } } void testDynamicQObject2() { - DynamicQObjectFactory factory {{SignalDefinition{"fooChanged", {QMetaType::Int}}}, - {SlotDefinition{"foo", QMetaType::Int, {}}, SlotDefinition{"setFoo", QMetaType::Void, {QMetaType::Int}}}, - {PropertyDefinition{"foo", QMetaType::Int, "foo", "setFoo", "fooChanged"}}}; - std::unique_ptr dynamicQObject(factory.create([](int, const QString&, const std::vector&)-> QVariant{})); + DOS::DynamicQObjectFactory factory {{DOS::SignalDefinition{"fooChanged", {QMetaType::Int}}}, + {DOS::SlotDefinition{"foo", QMetaType::Int, {}}, DOS::SlotDefinition{"setFoo", QMetaType::Void, {QMetaType::Int}}}, + {DOS::PropertyDefinition{"foo", QMetaType::Int, "foo", "setFoo", "fooChanged"}}}; + std::unique_ptr dynamicQObject(factory.create([](const QString&, const std::vector&)-> QVariant{})); QVERIFY(dynamicQObject != nullptr); QSignalSpy signalSpy(dynamicQObject.get(), SIGNAL(fooChanged(int)));