Temporary removed QAbstractListModel support and improved unit tests
This commit is contained in:
parent
711131b4f4
commit
8f188552c2
|
@ -14,19 +14,16 @@ find_package(Qt5Quick)
|
|||
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
|
||||
include/DOtherSide/DynamicSlot.h
|
||||
include/DOtherSide/DOtherSide.h
|
||||
include/DOtherSide/DynamicQObject.h
|
||||
include/DOtherSide/IDynamicQObject.h
|
||||
include/DOtherSide/OnSlotExecutedHandler.h
|
||||
include/DOtherSide/DynamicQObjectFactory.h
|
||||
include/DOtherSide/DynamicQObject2.h
|
||||
include/DOtherSide/DynamicQObject.h
|
||||
include/DOtherSide/Utils.h
|
||||
)
|
||||
|
||||
|
@ -37,7 +34,7 @@ set(SRC_LIST
|
|||
src/DynamicSignal.cpp
|
||||
src/DynamicProperty.cpp
|
||||
src/DynamicQObjectFactory.cpp
|
||||
src/DynamicQObject2.cpp
|
||||
src/DynamicQObject.cpp
|
||||
src/DOtherSideTypesCpp.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "DOtherSide/DynamicQObject.h"
|
||||
|
||||
using BaseQObject = DynamicQObject<QObject>;
|
|
@ -85,20 +85,18 @@ DOS_API void dos_qvariant_isnull(void* vptr, bool* result);
|
|||
DOS_API void dos_qvariant_delete(void* vptr);
|
||||
DOS_API void dos_qvariant_assign(void* vptr, void* other);
|
||||
|
||||
// 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);
|
||||
|
||||
// QObject
|
||||
DOS_API void dos_qobject_create(void** vptr,
|
||||
void* dObjectPointer,
|
||||
DObjectCallback dObjectCallback);
|
||||
DOS_API void dos_qobject_slot_create(void* vptr,
|
||||
const char* name,
|
||||
int parametersCount,
|
||||
int* parametersMetaTypes,
|
||||
int* slotIndex);
|
||||
DOS_API void dos_qobject_signal_create(void* vptr,
|
||||
const char* name,
|
||||
int parametersCount,
|
||||
int* parametersMetaTypes,
|
||||
int* signalIndex);
|
||||
DOS_API void dos_qobject_signal_emit(void* vptr,
|
||||
const char* name,
|
||||
int parametersCount,
|
||||
|
@ -114,12 +112,6 @@ DOS_API void dos_qobject_signal_disconnect(void* senderVPtr,
|
|||
void* receiverVPtr,
|
||||
const char* method,
|
||||
bool* result);
|
||||
DOS_API void dos_qobject_property_create(void* vptr,
|
||||
const char* name,
|
||||
int propertyMetaType,
|
||||
const char* readSlot,
|
||||
const char* writeSlot,
|
||||
const char* notifySignal);
|
||||
DOS_API void dos_qobject_objectName(void* vptr, char** result);
|
||||
DOS_API void dos_qobject_findChild(void* vptr, const char* name, int options, void** child);
|
||||
DOS_API void dos_qobject_delete(void* vptr);
|
||||
|
@ -141,26 +133,6 @@ DOS_API void dos_qhash_int_qbytearray_delete(QHashIntQByteArrayVoidPtr vptr);
|
|||
DOS_API void dos_qhash_int_qbytearray_insert(QHashIntQByteArrayVoidPtr vptr, int key, const char* value);
|
||||
DOS_API void dos_qhash_int_qbytearray_value(QHashIntQByteArrayVoidPtr vptr, int key, char** result);
|
||||
|
||||
// QAbstractListModel
|
||||
DOS_API void dos_qabstractlistmodel_create(void** vptr,
|
||||
void* callbackObject,
|
||||
DObjectCallback dObjectCallback,
|
||||
RowCountCallback rowCountCallback,
|
||||
ColumnCountCallback columnCountCallback,
|
||||
DataCallback dataCallback,
|
||||
SetDataCallback setDataCallback,
|
||||
RoleNamesCallback roleNamesCallback,
|
||||
FlagsCallback flagsCallback,
|
||||
HeaderDataCallback headerDataCallback);
|
||||
DOS_API void dos_qabstractlistmodel_beginInsertRows(void* vptr, void* parent, int first, int last);
|
||||
DOS_API void dos_qabstractlistmodel_endInsertRows(void* vptr);
|
||||
DOS_API void dos_qabstractlistmodel_beginRemoveRows(void* vptr, void* parent, int first, int last);
|
||||
DOS_API void dos_qabstractlistmodel_endRemoveRows(void* vptr);
|
||||
DOS_API void dos_qabstractlistmodel_beginResetModel(void* vptr);
|
||||
DOS_API void dos_qabstractlistmodel_endResetModel(void* vptr);
|
||||
DOS_API void dos_qabstractlistmodel_dataChanged(void* vptr, void* topLeft, void* bottomRight, int* rolesPtr, int rolesLength);
|
||||
DOS_API void dos_qabstractlistmodel_delete(void* vptr);
|
||||
|
||||
// QResource
|
||||
DOS_API void dos_qresource_register(const char* filename);
|
||||
|
||||
|
@ -169,17 +141,6 @@ 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
|
||||
|
|
|
@ -1,434 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
// std
|
||||
#include <memory>
|
||||
#include <array>
|
||||
// Qt
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QScopedPointer>
|
||||
#include <QtCore/QDebug>
|
||||
#include "private/qmetaobjectbuilder_p.h"
|
||||
// DOtherSide
|
||||
#include "DOtherSide/DynamicSignal.h"
|
||||
#include "DOtherSide/DynamicSlot.h"
|
||||
#include "DOtherSide/DynamicProperty.h"
|
||||
#include <QObject>
|
||||
#include <functional>
|
||||
|
||||
#include "DOtherSide/IDynamicQObject.h"
|
||||
|
||||
/// This class implements a QObject to which signals, slots and properties can be added dynamically
|
||||
template <class T>
|
||||
class DynamicQObject : public T, public IDynamicQObject
|
||||
namespace DOS
|
||||
{
|
||||
using SafeQMetaObjectPtr = std::unique_ptr<QMetaObject, void(*)(void*)>;
|
||||
using OnSlotExecutedHandler = std::function<QVariant(const DynamicSlot&, const std::vector<QVariant>&)>;
|
||||
|
||||
class DynamicQObjectFactory;
|
||||
|
||||
class DynamicQObject : public QObject, public IDynamicQObject
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
DynamicQObject();
|
||||
using OnSlotExecuted = std::function<QVariant(const QString&, const std::vector<QVariant>&)>;
|
||||
|
||||
/// Destructor
|
||||
virtual ~DynamicQObject();
|
||||
DynamicQObject(const DynamicQObjectFactory* factory,
|
||||
OnSlotExecuted handler);
|
||||
|
||||
/// Sets the on slot executed handler
|
||||
void setOnSlotExecutedHandler(const OnSlotExecutedHandler& handler);
|
||||
|
||||
/// Register a new signal
|
||||
bool registerSignal(const QString& name,
|
||||
const QList<QMetaType::Type>& argumentsTypes,
|
||||
int& signalIndex) override;
|
||||
|
||||
/// Register a new slot
|
||||
bool registerSlot(const QString& name,
|
||||
const QMetaType::Type returnType,
|
||||
const QList<QMetaType::Type>& argumentsTypes,
|
||||
int& slotIndex) override;
|
||||
|
||||
/// Register a new property
|
||||
bool registerProperty(const QString& name,
|
||||
QMetaType::Type type,
|
||||
const QString& readSlotName,
|
||||
const QString& writeSlotName = "",
|
||||
const QString& notifySignalName = "") override;
|
||||
|
||||
/// Emit the signal with the given name and arguments
|
||||
bool emitSignal(const QString& name, const QList<QVariant>& argumentsValues) override;
|
||||
|
||||
/// Return the QMetaObject for this DynamicQObject
|
||||
bool emitSignal(const QString& name, const std::vector<QVariant>& arguments) override;
|
||||
const QMetaObject* metaObject() const override;
|
||||
|
||||
/// The qt metacall. Called from Qt when a signals, slot or property is invoked
|
||||
int qt_metacall(QMetaObject::Call, int, void**) override;
|
||||
int qt_metacall(QMetaObject::Call callType, int index, void**args) override;
|
||||
|
||||
private:
|
||||
bool executeSlot(const DynamicSlot& slot, void** args);
|
||||
bool executeSlot(int index, void** args);
|
||||
bool readProperty(int index, void** args);
|
||||
bool writeProperty(int index, void** args);
|
||||
|
||||
bool readProperty(const DynamicProperty& property, void** args);
|
||||
|
||||
bool writeProperty(const DynamicProperty& property, void** args);
|
||||
|
||||
/// Function for recreating the metaobject starting from another one.
|
||||
/// The creation is customizable by injecting custom code after signals and slots have
|
||||
/// been added
|
||||
static QMetaObject* recreateMetaObjectBuilder(QMetaObject* currentMetaObject,
|
||||
const std::function<void(QMetaObjectBuilder&)>& afterSignalAdded,
|
||||
const std::function<void(QMetaObjectBuilder&)>& afterSlotAdded,
|
||||
const std::function<void(QMetaObjectBuilder&)>& afterPropertyAdded);
|
||||
|
||||
SafeQMetaObjectPtr m_metaObject;
|
||||
QHash<QString, DynamicSignal> m_signalsByName;
|
||||
QHash<QByteArray, DynamicSignal> m_signalsBySignature;
|
||||
QHash<QString, DynamicSlot> m_slotsByName;
|
||||
QHash<QByteArray, DynamicSlot> m_slotsBySignature;
|
||||
QHash<QByteArray, DynamicProperty> m_propertiesByName;
|
||||
OnSlotExecutedHandler m_onSlotExecutedHandler;
|
||||
const DynamicQObjectFactory* const m_factory;
|
||||
const OnSlotExecuted m_handler;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
DynamicQObject<T>::DynamicQObject()
|
||||
: T()
|
||||
, m_metaObject(nullptr, ::free)
|
||||
{
|
||||
QMetaObjectBuilder builder;
|
||||
builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
|
||||
builder.setClassName("DynamicQObject");
|
||||
builder.setSuperClass(&T::staticMetaObject);
|
||||
m_metaObject.reset(builder.toMetaObject());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DynamicQObject<T>::setOnSlotExecutedHandler(const typename DynamicQObject::OnSlotExecutedHandler &handler)
|
||||
{
|
||||
m_onSlotExecutedHandler = handler;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DynamicQObject<T>::~DynamicQObject() = default;
|
||||
|
||||
template <typename T>
|
||||
bool DynamicQObject<T>::registerSlot(const QString& name,
|
||||
const QMetaType::Type returnType,
|
||||
const QList<QMetaType::Type>& argumentsTypes,
|
||||
int& slotIndex)
|
||||
{
|
||||
DynamicSlot slot(name, returnType, argumentsTypes);
|
||||
|
||||
if (m_slotsBySignature.contains(slot.signature()))
|
||||
return false;
|
||||
|
||||
m_slotsByName.insertMulti(slot.name(), slot);
|
||||
m_slotsBySignature[slot.signature()] = slot;
|
||||
|
||||
auto afterSignalAdded = [](QMetaObjectBuilder&) {};
|
||||
auto afterPropertyAdded = afterSignalAdded;
|
||||
|
||||
auto afterSlotAdded = [&slot, returnType](QMetaObjectBuilder & metaObjectBuilder) {
|
||||
QMetaMethodBuilder methodBuilder = metaObjectBuilder.addSlot(slot.signature());
|
||||
methodBuilder.setReturnType(QMetaType::typeName(returnType));
|
||||
methodBuilder.setAttributes(QMetaMethod::Scriptable);
|
||||
};
|
||||
|
||||
auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.get(),
|
||||
afterSignalAdded,
|
||||
afterSlotAdded,
|
||||
afterPropertyAdded);
|
||||
|
||||
m_metaObject.reset(newMetaObject);
|
||||
|
||||
slotIndex = m_metaObject->indexOfSlot(QMetaObject::normalizedSignature(slot.signature()));
|
||||
|
||||
return slotIndex != -1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DynamicQObject<T>::registerSignal(const QString& name, const QList<QMetaType::Type>& arguments, int& signalIndex)
|
||||
{
|
||||
DynamicSignal signal(name, arguments);
|
||||
|
||||
if (m_signalsBySignature.contains(signal.signature()))
|
||||
return false;
|
||||
|
||||
m_signalsByName.insertMulti(signal.name(), signal);
|
||||
m_signalsBySignature[signal.signature()] = signal;
|
||||
|
||||
auto afterSignalAdded = [&signal](QMetaObjectBuilder & metaObjectBuilder) {
|
||||
QMetaMethodBuilder methodBuilder = metaObjectBuilder.addSignal(signal.signature());
|
||||
methodBuilder.setReturnType(QMetaType::typeName(QMetaType::Void));
|
||||
methodBuilder.setAccess(QMetaMethod::Public);
|
||||
};
|
||||
|
||||
auto afterSlotAdded = [](QMetaObjectBuilder&) {};
|
||||
auto afterPropertyAdded = afterSlotAdded;
|
||||
|
||||
auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.get(),
|
||||
afterSignalAdded,
|
||||
afterSlotAdded,
|
||||
afterPropertyAdded);
|
||||
|
||||
m_metaObject.reset(newMetaObject);
|
||||
|
||||
signalIndex = m_metaObject->indexOfSignal(QMetaObject::normalizedSignature(signal.signature()));
|
||||
|
||||
return signalIndex != -1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DynamicQObject<T>::registerProperty(const QString& name,
|
||||
QMetaType::Type type,
|
||||
const QString& readSlotName,
|
||||
const QString& writeSlotName,
|
||||
const QString& notifySignalName)
|
||||
{
|
||||
DynamicProperty property(name, type, readSlotName, writeSlotName, notifySignalName);
|
||||
|
||||
DynamicSignal notifySignal;
|
||||
|
||||
if (!notifySignalName.isEmpty())
|
||||
{
|
||||
notifySignal = m_signalsByName.value(notifySignalName, DynamicSignal());
|
||||
if (!notifySignal.isValid())
|
||||
return false;
|
||||
}
|
||||
|
||||
m_propertiesByName.insert(name.toUtf8(), property);
|
||||
|
||||
auto afterSignalAdded = [](QMetaObjectBuilder & metaObjectBuilder) { Q_UNUSED(metaObjectBuilder); };
|
||||
auto afterSlotAdded = [](QMetaObjectBuilder & metaObjectBuilder) { Q_UNUSED(metaObjectBuilder); };
|
||||
auto afterPropertyAdded = [name, type, notifySignal](QMetaObjectBuilder & metaObjectBuilder)
|
||||
{
|
||||
int signalIndex = -1;
|
||||
if (notifySignal.isValid())
|
||||
{
|
||||
for (int i = 0; i < metaObjectBuilder.methodCount(); ++i)
|
||||
{
|
||||
QMetaMethodBuilder methodBuilder = metaObjectBuilder.method(i);
|
||||
if (methodBuilder.methodType() == QMetaMethod::Signal)
|
||||
{
|
||||
if (methodBuilder.signature() == QMetaObject::normalizedSignature(notifySignal.signature()))
|
||||
{
|
||||
signalIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto typeName = QMetaType::typeName(type);
|
||||
auto builder = metaObjectBuilder.addProperty(name.toUtf8(),
|
||||
QMetaObject::normalizedType(typeName),
|
||||
signalIndex);
|
||||
if (signalIndex == -1)
|
||||
builder.setConstant(true);
|
||||
};
|
||||
|
||||
auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.get()
|
||||
, afterSignalAdded
|
||||
, afterSlotAdded
|
||||
, afterPropertyAdded);
|
||||
m_metaObject.reset(newMetaObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DynamicQObject<T>::emitSignal(const QString& name, const QList<QVariant>& args)
|
||||
{
|
||||
DynamicSignal signal;
|
||||
|
||||
for (DynamicSignal currentSignal : m_signalsByName.values(name))
|
||||
{
|
||||
if (currentSignal.validate(args))
|
||||
{
|
||||
signal = currentSignal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!signal.isValid())
|
||||
return false;
|
||||
|
||||
int index = m_metaObject->indexOfSignal(QMetaObject::normalizedSignature(signal.signature()));
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
QVariantList argsCopy = args;
|
||||
|
||||
QVector<void*> arguments(argsCopy.size() + 1 , 0);
|
||||
arguments[0] = 0;
|
||||
for (int i = 0; i < argsCopy.size(); ++i)
|
||||
arguments[i + 1] = &argsCopy[i];
|
||||
|
||||
QMetaObject::activate(this, index, arguments.data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const QMetaObject* DynamicQObject<T>::metaObject() const
|
||||
{
|
||||
return m_metaObject.get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DynamicQObject<T>::executeSlot(const DynamicSlot& slot, void** args)
|
||||
{
|
||||
if (!slot.isValid())
|
||||
return false;
|
||||
|
||||
std::vector<QVariant> arguments;
|
||||
arguments.reserve(slot.argumentsTypes().size());
|
||||
for (int i = 0; i < slot.argumentsTypes().count(); ++i)
|
||||
arguments.emplace_back(QVariant(slot.argumentTypeAt(i), args[i + 1]));
|
||||
|
||||
QVariant result = m_onSlotExecutedHandler(slot, arguments);
|
||||
|
||||
if (slot.returnType() != QMetaType::Void && result.isValid())
|
||||
{
|
||||
QMetaType metatype(slot.returnType());
|
||||
metatype.construct(args[0], result.constData());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DynamicQObject<T>::readProperty(const DynamicProperty& property, void** args)
|
||||
{
|
||||
if (!property.isValid())
|
||||
return false;
|
||||
|
||||
if (!property.isReadable())
|
||||
return false;
|
||||
|
||||
DynamicSlot readSlot = m_slotsByName.value(property.readSlot(), DynamicSlot());
|
||||
|
||||
if (!readSlot.isValid())
|
||||
return false;
|
||||
|
||||
if (readSlot.argumentsTypes().count() > 0)
|
||||
return false;
|
||||
|
||||
if (readSlot.returnType() != property.type())
|
||||
return false;
|
||||
|
||||
return executeSlot(readSlot, args);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DynamicQObject<T>::writeProperty(const DynamicProperty& property, void** args)
|
||||
{
|
||||
if (!property.isValid())
|
||||
return false;
|
||||
|
||||
if (!property.isWriteable())
|
||||
return false;
|
||||
|
||||
DynamicSlot writeSlot = m_slotsByName.value(property.writeSlot(), DynamicSlot());
|
||||
|
||||
if (!writeSlot.isValid())
|
||||
return false;
|
||||
|
||||
if (writeSlot.argumentsTypes().count() != 1)
|
||||
return false;
|
||||
|
||||
if (writeSlot.returnType() != QMetaType::Void)
|
||||
return false;
|
||||
|
||||
void* sortedArgs[2]; // The write property is peculiar because it has
|
||||
sortedArgs[0] = args[2]; // the actual value in pos 0 and result in 2
|
||||
sortedArgs[1] = args[0]; // We reorder it for having the result in 0 and
|
||||
executeSlot(writeSlot, sortedArgs); // first arg in 1
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int DynamicQObject<T>::qt_metacall(QMetaObject::Call callType, int index, void** args)
|
||||
{
|
||||
switch (callType)
|
||||
{
|
||||
case QMetaObject::InvokeMetaMethod: {
|
||||
QMetaMethod method = m_metaObject->method(index);
|
||||
|
||||
if (!method.isValid())
|
||||
return -1;
|
||||
|
||||
DynamicSlot slot = m_slotsBySignature[method.methodSignature()];
|
||||
return executeSlot(slot, args) ? 1 : -1;
|
||||
}
|
||||
|
||||
case QMetaObject::ReadProperty: {
|
||||
QMetaProperty metaProperty = m_metaObject->property(index);
|
||||
|
||||
if (!metaProperty.isValid())
|
||||
return -1;
|
||||
|
||||
DynamicProperty dynamicProperty = m_propertiesByName.value(metaProperty.name(), DynamicProperty());
|
||||
return readProperty(dynamicProperty, args) ? 1 : -1;
|
||||
}
|
||||
|
||||
case QMetaObject::WriteProperty: {
|
||||
QMetaProperty metaProperty = m_metaObject->property(index);
|
||||
|
||||
if (!metaProperty.isValid())
|
||||
return -1;
|
||||
|
||||
DynamicProperty dynamicProperty = m_propertiesByName.value(metaProperty.name(), DynamicProperty());
|
||||
return writeProperty(dynamicProperty, args) ? 1 : -1;
|
||||
}
|
||||
|
||||
default:
|
||||
return T::qt_metacall(callType, index, args);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QMetaObject* DynamicQObject<T>::recreateMetaObjectBuilder(QMetaObject* currentMetaObject,
|
||||
const std::function<void(QMetaObjectBuilder&)>& afterSignalAdded,
|
||||
const std::function<void(QMetaObjectBuilder&)>& afterSlotAdded,
|
||||
const std::function<void(QMetaObjectBuilder&)>& afterPropertyAdded)
|
||||
{
|
||||
// Collect the current methods and signals
|
||||
QList<QMetaMethod> signalsList;
|
||||
QList<QMetaMethod> methodsList;
|
||||
QList<QMetaProperty> propertiesList;
|
||||
|
||||
for (int i = currentMetaObject->methodOffset(); i < currentMetaObject->methodCount(); ++i)
|
||||
{
|
||||
QMetaMethod method = currentMetaObject->method(i);
|
||||
if (method.methodType() == QMetaMethod::Signal)
|
||||
signalsList.append(method);
|
||||
else
|
||||
methodsList.append(method);
|
||||
}
|
||||
|
||||
for (int i = currentMetaObject->propertyOffset(); i < currentMetaObject->propertyCount(); ++i)
|
||||
{
|
||||
QMetaProperty property = currentMetaObject->property(i);
|
||||
propertiesList.append(property);
|
||||
}
|
||||
|
||||
QMetaObjectBuilder metaObjectBuilder;
|
||||
metaObjectBuilder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
|
||||
metaObjectBuilder.setClassName(currentMetaObject->className());
|
||||
metaObjectBuilder.setSuperClass(currentMetaObject->superClass());
|
||||
|
||||
for (auto& method : signalsList)
|
||||
metaObjectBuilder.addMethod(method);
|
||||
|
||||
// Call custom code to be executed after signal have been added
|
||||
afterSignalAdded(metaObjectBuilder);
|
||||
|
||||
for (auto& method : methodsList)
|
||||
metaObjectBuilder.addMethod(method);
|
||||
|
||||
// Call custom code to be executed after slots have been added
|
||||
afterSlotAdded(metaObjectBuilder);
|
||||
|
||||
for (auto& property : propertiesList)
|
||||
metaObjectBuilder.addProperty(property);
|
||||
|
||||
afterPropertyAdded(metaObjectBuilder);
|
||||
|
||||
return metaObjectBuilder.toMetaObject();
|
||||
}
|
||||
} // namespace DOS
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <functional>
|
||||
|
||||
namespace DOS
|
||||
{
|
||||
|
||||
class DynamicQObjectFactory;
|
||||
|
||||
class DynamicQObject2 : public QObject
|
||||
{
|
||||
public:
|
||||
using OnSlotExecuted = std::function<QVariant(const QString&, const std::vector<QVariant>&)>;
|
||||
|
||||
DynamicQObject2(const DynamicQObjectFactory* factory,
|
||||
OnSlotExecuted handler);
|
||||
|
||||
void emitSignal(const QString& name, const std::vector<QVariant>& arguments);
|
||||
const QMetaObject* metaObject() const override;
|
||||
int qt_metacall(QMetaObject::Call callType, int index, void**args) override;
|
||||
|
||||
private:
|
||||
bool executeSlot(int index, void** args);
|
||||
bool readProperty(int index, void** args);
|
||||
bool writeProperty(int index, void** args);
|
||||
|
||||
const DynamicQObjectFactory* const m_factory;
|
||||
const OnSlotExecuted m_handler;
|
||||
};
|
||||
|
||||
} // namespace DOS
|
|
@ -13,7 +13,7 @@
|
|||
namespace DOS
|
||||
{
|
||||
|
||||
class DynamicQObject2;
|
||||
class DynamicQObject;
|
||||
|
||||
class DynamicQObjectFactory
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ public:
|
|||
SlotDefinitions slotDefinitions,
|
||||
PropertyDefinitions propertyDefinitions);
|
||||
|
||||
DynamicQObject2* create(OnSlotExecuted handler) const;
|
||||
DynamicQObject* create(OnSlotExecuted handler) const;
|
||||
inline const QMetaObject* metaObject() const;
|
||||
inline int signalSlotIndex(const QString& signalName) const;
|
||||
inline int readSlotIndex(const char* propertyName) const;
|
||||
|
|
|
@ -3,29 +3,9 @@
|
|||
class IDynamicQObject
|
||||
{
|
||||
public:
|
||||
using Callback = void (*)(void*, void*, int, void**);
|
||||
|
||||
/// Destructor
|
||||
virtual ~IDynamicQObject() = default;
|
||||
|
||||
/// Register a new signal
|
||||
virtual bool registerSignal(const QString& name,
|
||||
const QList<QMetaType::Type>& argumentsTypes,
|
||||
int& signalIndex) = 0;
|
||||
|
||||
/// Register a new slot
|
||||
virtual bool registerSlot(const QString& name,
|
||||
const QMetaType::Type returnType,
|
||||
const QList<QMetaType::Type>& argumentsTypes,
|
||||
int& slotIndex) = 0;
|
||||
|
||||
/// Register a new property
|
||||
virtual bool registerProperty(const QString& name,
|
||||
QMetaType::Type type,
|
||||
const QString& readSlotName,
|
||||
const QString& writeSlotName = "",
|
||||
const QString& notifySignalName = "") = 0;
|
||||
|
||||
/// Emit the signal with the given name and arguments
|
||||
virtual bool emitSignal(const QString& name, const QList<QVariant>& argumentsValues) = 0;
|
||||
virtual bool emitSignal(const QString& name, const std::vector<QVariant>& argumentsValues) = 0;
|
||||
};
|
||||
|
|
|
@ -12,12 +12,12 @@ class DynamicSlot;
|
|||
class OnSlotExecutedHandler
|
||||
{
|
||||
public:
|
||||
OnSlotExecutedHandler(void* dObjectPointer, IDynamicQObject::Callback dObjectCallback);
|
||||
using Callback = void (*)(void*, void*, int, void**);
|
||||
|
||||
OnSlotExecutedHandler(void* dObjectPointer, Callback dObjectCallback);
|
||||
|
||||
QVariant operator()(const QString& name, const std::vector<QVariant>& args);
|
||||
QVariant operator()(const DynamicSlot& slot, const std::vector<QVariant>& args);
|
||||
|
||||
private:
|
||||
void* m_dObjectPointer;
|
||||
IDynamicQObject::Callback m_dObjectCallback;
|
||||
Callback m_dObjectCallback;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace DOS
|
||||
{
|
||||
|
@ -23,10 +25,20 @@ wrapped_array<T> wrap_array(T* first, std::ptrdiff_t size) noexcept
|
|||
{ return {first, size}; }
|
||||
|
||||
template <typename T, typename G>
|
||||
std::vector<T> toVector(const wrapped_array<G>& array)
|
||||
std::vector<T> toVector(G* first, std::ptrdiff_t size) noexcept
|
||||
{
|
||||
const wrapped_array<G> array = wrap_array(first, size);
|
||||
std::vector<T> result;
|
||||
std::copy(array.begin(), array.end(), result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename K, typename R = typename std::result_of<K(T)>::type>
|
||||
std::vector<R> toVector(T* first, std::ptrdiff_t size, K f) noexcept
|
||||
{
|
||||
wrapped_array<T> array = wrap_array<T>(first, size);
|
||||
std::vector<R> result;
|
||||
std::transform(array.begin(), array.end(), result.begin(), f);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,9 @@
|
|||
#include <QtWidgets/QApplication>
|
||||
|
||||
#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"
|
||||
#include "DOtherSide/DynamicQObject.h"
|
||||
|
||||
|
||||
void convert_to_cstring(const QString& source, char** destination)
|
||||
|
@ -353,50 +350,11 @@ void dos_qvariant_setQAbstractListModel(void* vptr, void* value)
|
|||
variant->setValue<QObject*>(qobject);
|
||||
}
|
||||
|
||||
void dos_qobject_create(void** vptr, void* dObjectPointer, DObjectCallback dObjectCallback)
|
||||
{
|
||||
auto dynamicQObject = new BaseQObject();
|
||||
QQmlEngine::setObjectOwnership(dynamicQObject, QQmlEngine::CppOwnership);
|
||||
dynamicQObject->setOnSlotExecutedHandler(OnSlotExecutedHandler(dObjectPointer, dObjectCallback));
|
||||
*vptr = dynamicQObject;
|
||||
}
|
||||
|
||||
void dos_qobject_delete(void* vptr)
|
||||
{
|
||||
auto dynamicQObject = reinterpret_cast<BaseQObject*>(vptr);
|
||||
dynamicQObject->disconnect();
|
||||
delete dynamicQObject;
|
||||
}
|
||||
|
||||
void dos_qobject_slot_create(void* vptr, const char* name, int parametersCount, int* parametersMetaTypes, int* slotIndex)
|
||||
{
|
||||
if (parametersCount <= 0)
|
||||
return;
|
||||
|
||||
auto qobject = reinterpret_cast<QObject*>(vptr);
|
||||
auto dynamicQObject = dynamic_cast<IDynamicQObject*>(qobject);
|
||||
|
||||
QMetaType::Type returnType = static_cast<QMetaType::Type>(parametersMetaTypes[0]);
|
||||
QList<QMetaType::Type> argumentsTypes;
|
||||
for (int i = 1; i < parametersCount; ++i)
|
||||
argumentsTypes << static_cast<QMetaType::Type>(parametersMetaTypes[i]);
|
||||
|
||||
dynamicQObject->registerSlot(QString::fromStdString(name), returnType, argumentsTypes, *slotIndex);
|
||||
}
|
||||
|
||||
void dos_qobject_signal_create(void* vptr, const char* name, int parametersCount, int* parametersMetaTypes, int* signalIndex)
|
||||
{
|
||||
if (parametersCount < 0)
|
||||
return;
|
||||
|
||||
auto qobject = reinterpret_cast<QObject*>(vptr);
|
||||
auto dynamicQObject = dynamic_cast<IDynamicQObject*>(qobject);
|
||||
|
||||
QList<QMetaType::Type> argumentsTypes;
|
||||
for (int i = 0; i < parametersCount; ++i)
|
||||
argumentsTypes << static_cast<QMetaType::Type>(parametersMetaTypes[i]);
|
||||
|
||||
dynamicQObject->registerSignal(QString::fromStdString(name), argumentsTypes, *signalIndex);
|
||||
qobject->disconnect();
|
||||
delete qobject;
|
||||
}
|
||||
|
||||
void dos_qobject_signal_emit(void* vptr, const char* name, int parametersCount, void** parameters)
|
||||
|
@ -404,13 +362,12 @@ void dos_qobject_signal_emit(void* vptr, const char* name, int parametersCount,
|
|||
auto qobject = reinterpret_cast<QObject*>(vptr);
|
||||
auto dynamicQObject = dynamic_cast<IDynamicQObject*>(qobject);
|
||||
|
||||
QVariantList arguments;
|
||||
for (int i = 0; i < parametersCount; ++i)
|
||||
arguments << *(reinterpret_cast<QVariant*>(parameters[i]));
|
||||
|
||||
dynamicQObject->emitSignal(QString::fromStdString(name), arguments);
|
||||
auto transformation = [](void* vptr)->QVariant{return *(reinterpret_cast<QVariant*>(vptr));};
|
||||
const std::vector<QVariant> variants = DOS::toVector(parameters, parametersCount, transformation);
|
||||
dynamicQObject->emitSignal(QString::fromStdString(name), variants);
|
||||
}
|
||||
|
||||
|
||||
void dos_qobject_signal_connect(void* senderVPtr,
|
||||
const char* signal,
|
||||
void* receiverVPtr,
|
||||
|
@ -420,11 +377,7 @@ void dos_qobject_signal_connect(void* senderVPtr,
|
|||
{
|
||||
auto sender = reinterpret_cast<QObject*>(senderVPtr);
|
||||
auto receiver = reinterpret_cast<QObject*>(receiverVPtr);
|
||||
*result = QObject::connect(sender,
|
||||
signal,
|
||||
receiver,
|
||||
method,
|
||||
(Qt::ConnectionType) type);
|
||||
*result = QObject::connect(sender, signal, receiver, method, (Qt::ConnectionType) type);
|
||||
}
|
||||
|
||||
void dos_qobject_signal_disconnect(void* senderVPtr,
|
||||
|
@ -435,26 +388,7 @@ void dos_qobject_signal_disconnect(void* senderVPtr,
|
|||
{
|
||||
auto sender = reinterpret_cast<QObject*>(senderVPtr);
|
||||
auto receiver = reinterpret_cast<QObject*>(receiverVPtr);
|
||||
*result = QObject::disconnect(sender,
|
||||
signal,
|
||||
receiver,
|
||||
method);
|
||||
}
|
||||
|
||||
void dos_qobject_property_create(void* vptr,
|
||||
const char* name,
|
||||
int type,
|
||||
const char* readSlot,
|
||||
const char* writeSlot,
|
||||
const char* notifySignal)
|
||||
{
|
||||
auto qobject = reinterpret_cast<QObject*>(vptr);
|
||||
auto dynamicQObject = dynamic_cast<IDynamicQObject*>(qobject);
|
||||
dynamicQObject->registerProperty(QString(name),
|
||||
QMetaType::Type(type),
|
||||
QString(readSlot),
|
||||
QString(writeSlot),
|
||||
QString(notifySignal));
|
||||
*result = QObject::disconnect(sender, signal, receiver, method);
|
||||
}
|
||||
|
||||
void dos_qobject_objectName(void* vptr, char** result)
|
||||
|
@ -527,86 +461,6 @@ void dos_qmodelindex_sibling(void* vptr, int row, int column, void* sibling)
|
|||
*siblingIndex = index->sibling(row, column);
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_create(void** vptr,
|
||||
void* dObjectPointer,
|
||||
DObjectCallback dObjectCallback,
|
||||
RowCountCallback rowCountCallback,
|
||||
ColumnCountCallback columnCountCallback,
|
||||
DataCallback dataCallback,
|
||||
SetDataCallback setDataCallback,
|
||||
RoleNamesCallback roleNamesCallaback,
|
||||
FlagsCallback flagsCallback,
|
||||
HeaderDataCallback headerDataCallback)
|
||||
{
|
||||
auto model = new BaseQAbstractListModel(dObjectPointer,
|
||||
rowCountCallback,
|
||||
columnCountCallback,
|
||||
dataCallback,
|
||||
setDataCallback,
|
||||
roleNamesCallaback,
|
||||
flagsCallback,
|
||||
headerDataCallback);
|
||||
model->setOnSlotExecutedHandler(OnSlotExecutedHandler(dObjectPointer, dObjectCallback));
|
||||
*vptr = model;
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_delete(void* vptr)
|
||||
{
|
||||
auto model = reinterpret_cast<BaseQAbstractListModel*>(vptr);
|
||||
delete model;
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_beginInsertRows(void* vptr, QModelIndexVoidPtr parentIndex, int first, int last)
|
||||
{
|
||||
auto model = reinterpret_cast<BaseQAbstractListModel*>(vptr);
|
||||
auto index = reinterpret_cast<QModelIndex*>(parentIndex);
|
||||
model->publicBeginInsertRows(*index, first, last);
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_endInsertRows(void* vptr)
|
||||
{
|
||||
auto model = reinterpret_cast<BaseQAbstractListModel*>(vptr);
|
||||
model->publicEndInsertRows();
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_beginRemoveRows(void* vptr, QModelIndexVoidPtr parentIndex, int first, int last)
|
||||
{
|
||||
auto model = reinterpret_cast<BaseQAbstractListModel*>(vptr);
|
||||
auto index = reinterpret_cast<QModelIndex*>(parentIndex);
|
||||
model->publicBeginRemoveRows(*index, first, last);
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_endRemoveRows(void* vptr)
|
||||
{
|
||||
auto model = reinterpret_cast<BaseQAbstractListModel*>(vptr);
|
||||
model->publicEndRemoveRows();
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_beginResetModel(void* vptr)
|
||||
{
|
||||
auto model = reinterpret_cast<BaseQAbstractListModel*>(vptr);
|
||||
model->publicBeginResetModel();
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_endResetModel(void* vptr)
|
||||
{
|
||||
auto model = reinterpret_cast<BaseQAbstractListModel*>(vptr);
|
||||
model->publicEndResetModel();
|
||||
}
|
||||
|
||||
void dos_qabstractlistmodel_dataChanged(void* vptr,
|
||||
QModelIndexVoidPtr topLeftIndex,
|
||||
QModelIndexVoidPtr bottomRightIndex,
|
||||
int* rolesArrayPtr,
|
||||
int rolesArrayLength)
|
||||
{
|
||||
auto model = reinterpret_cast<BaseQAbstractListModel*>(vptr);
|
||||
auto topLeft = reinterpret_cast<QModelIndex*>(topLeftIndex);
|
||||
auto bottomRight = reinterpret_cast<QModelIndex*>(bottomRightIndex);
|
||||
auto roles = QVector<int>::fromStdVector(std::vector<int>(rolesArrayPtr, rolesArrayPtr + rolesArrayLength));
|
||||
model->publicDataChanged(*topLeft, *bottomRight, roles);
|
||||
}
|
||||
|
||||
void dos_qhash_int_qbytearray_create(QHashIntQByteArrayVoidPtr* vptr)
|
||||
{
|
||||
*vptr = new QHash<int, QByteArray>();
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
#include "DOtherSide/DOtherSideTypesCpp.h"
|
||||
|
||||
namespace DOS {
|
||||
|
||||
namespace DOS
|
||||
{
|
||||
SignalDefinitions toVector(const ::SignalDefinitions& cType)
|
||||
{
|
||||
return toVector<SignalDefinition, ::SignalDefinition>(wrap_array(cType.definitions, cType.count));
|
||||
return toVector<SignalDefinition>(cType.definitions, cType.count);
|
||||
}
|
||||
|
||||
SlotDefinitions toVector(const ::SlotDefinitions& cType)
|
||||
{
|
||||
return toVector<SlotDefinition, ::SlotDefinition>(wrap_array(cType.definitions, cType.count));
|
||||
return toVector<SlotDefinition>(cType.definitions, cType.count);
|
||||
}
|
||||
|
||||
PropertyDefinitions toVector(const ::PropertyDefinitions& cType)
|
||||
{
|
||||
return toVector<PropertyDefinition, ::PropertyDefinition>(wrap_array(cType.definitions, cType.count));
|
||||
}
|
||||
return toVector<PropertyDefinition>(cType.definitions, cType.count);
|
||||
}
|
||||
} // namespace DOS
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "DOtherSide/DynamicQObject2.h"
|
||||
#include "DOtherSide/DynamicQObject.h"
|
||||
#include "DOtherSide/DynamicQObjectFactory.h"
|
||||
#include <QtCore/QMetaMethod>
|
||||
#include <QtCore/QDebug>
|
||||
|
@ -6,19 +6,19 @@
|
|||
namespace DOS
|
||||
{
|
||||
|
||||
DynamicQObject2::DynamicQObject2(const DynamicQObjectFactory *factory,
|
||||
DynamicQObject::DynamicQObject(const DynamicQObjectFactory *factory,
|
||||
OnSlotExecuted handler)
|
||||
: m_factory(factory)
|
||||
, m_handler(std::move(handler))
|
||||
{}
|
||||
|
||||
void DynamicQObject2::emitSignal(const QString &name, const std::vector<QVariant> &args)
|
||||
bool DynamicQObject::emitSignal(const QString &name, const std::vector<QVariant> &args)
|
||||
{
|
||||
const int index = metaObject()->methodOffset() + m_factory->signalSlotIndex(name);
|
||||
const QMetaMethod method = metaObject()->method(index);
|
||||
if (!method.isValid()) {
|
||||
qDebug() << "Cannot emit signal from invalid method";
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
Q_ASSERT(name.toUtf8() == method.name());
|
||||
|
||||
|
@ -27,14 +27,15 @@ void DynamicQObject2::emitSignal(const QString &name, const std::vector<QVariant
|
|||
auto func = [](const QVariant& arg) -> void* { return (void*)(&arg); };
|
||||
std::transform(args.begin(), args.end(), arguments.begin() + 1, func);
|
||||
QMetaObject::activate(this, method.methodIndex(), arguments.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
const QMetaObject *DynamicQObject2::metaObject() const
|
||||
const QMetaObject *DynamicQObject::metaObject() const
|
||||
{
|
||||
return m_factory->metaObject();
|
||||
}
|
||||
|
||||
int DynamicQObject2::qt_metacall(QMetaObject::Call callType, int index, void** args)
|
||||
int DynamicQObject::qt_metacall(QMetaObject::Call callType, int index, void** args)
|
||||
{
|
||||
switch (callType)
|
||||
{
|
||||
|
@ -51,7 +52,7 @@ int DynamicQObject2::qt_metacall(QMetaObject::Call callType, int index, void** a
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool DynamicQObject2::executeSlot(int index, void **args)
|
||||
bool DynamicQObject::executeSlot(int index, void **args)
|
||||
{
|
||||
const QMetaMethod method = metaObject()->method(index);
|
||||
if (!method.isValid()) {
|
||||
|
@ -75,7 +76,7 @@ bool DynamicQObject2::executeSlot(int index, void **args)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DynamicQObject2::readProperty(int index, void **args)
|
||||
bool DynamicQObject::readProperty(int index, void **args)
|
||||
{
|
||||
const QMetaProperty property = metaObject()->property(index);
|
||||
if (!property.isValid() || !property.isReadable()) {
|
||||
|
@ -85,7 +86,7 @@ bool DynamicQObject2::readProperty(int index, void **args)
|
|||
return executeSlot(m_factory->readSlotIndex(property.name()), args);
|
||||
}
|
||||
|
||||
bool DynamicQObject2::writeProperty(int index, void **args)
|
||||
bool DynamicQObject::writeProperty(int index, void **args)
|
||||
{
|
||||
const QMetaProperty property = metaObject()->property(index);
|
||||
if (!property.isValid() || !property.isWritable()) {
|
|
@ -1,5 +1,5 @@
|
|||
#include "DOtherSide/DynamicQObjectFactory.h"
|
||||
#include "DOtherSide/DynamicQObject2.h"
|
||||
#include "DOtherSide/DynamicQObject.h"
|
||||
#include "private/qmetaobjectbuilder_p.h"
|
||||
#include "private/qmetaobject_p.h"
|
||||
#include "private/qobject_p.h"
|
||||
|
@ -69,9 +69,9 @@ DynamicQObjectFactory::DynamicQObjectFactory(SignalDefinitions signalDefinitions
|
|||
m_metaObject.reset(builder.toMetaObject());
|
||||
}
|
||||
|
||||
DynamicQObject2* DynamicQObjectFactory::create(OnSlotExecuted handler) const
|
||||
DynamicQObject* DynamicQObjectFactory::create(OnSlotExecuted handler) const
|
||||
{
|
||||
return new DynamicQObject2(this, std::move(handler));
|
||||
return new DynamicQObject(this, std::move(handler));
|
||||
}
|
||||
|
||||
} // namespace DOS
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "DOtherSide/DynamicSlot.h"
|
||||
|
||||
OnSlotExecutedHandler::OnSlotExecutedHandler(void *dObjectPointer,
|
||||
IDynamicQObject::Callback dObjectCallback)
|
||||
Callback dObjectCallback)
|
||||
: m_dObjectPointer(dObjectPointer)
|
||||
, m_dObjectCallback(dObjectCallback)
|
||||
{}
|
||||
|
@ -29,8 +29,3 @@ QVariant OnSlotExecutedHandler::operator()(const QString &name, const std::vecto
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariant OnSlotExecutedHandler::operator()(const DynamicSlot& slot, const std::vector<QVariant> &args)
|
||||
{
|
||||
return operator ()(slot.name(), args);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
project(TestDynamicQObject)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/lib/include ${CMAKE_SOURCE_DIR}/lib/include/Qt)
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5Test REQUIRED)
|
||||
find_package(Qt5Quick REQUIRED)
|
||||
|
||||
add_executable(${PROJECT_NAME} test_dynamicqobject.cpp)
|
||||
add_executable(${PROJECT_NAME} Resources.qrc test_dynamicqobject.cpp main.qml)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)
|
||||
target_link_libraries(${PROJECT_NAME} DOtherSideStatic Qt5::Test)
|
||||
target_link_libraries(${PROJECT_NAME} DOtherSideStatic Qt5::Quick Qt5::Widgets Qt5::Test Qt5::Core)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>CMakeLists.txt</file>
|
||||
<file>main.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,8 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
ApplicationWindow {
|
||||
width: 100
|
||||
height: 100
|
||||
Component.onCompleted: visible = true
|
||||
}
|
|
@ -5,100 +5,130 @@
|
|||
#include <QDebug>
|
||||
#include <QTest>
|
||||
#include <QSignalSpy>
|
||||
#include <QTimer>
|
||||
#include <QApplication>
|
||||
#include <QQuickWindow>
|
||||
|
||||
// DOtherSide
|
||||
#include "DOtherSide/BaseQObject.h"
|
||||
#include "DOtherSide/DOtherSide.h"
|
||||
#include "DOtherSide/DynamicQObject.h"
|
||||
#include "DOtherSide/DynamicQObject2.h"
|
||||
#include "DOtherSide/DynamicQObjectFactory.h"
|
||||
|
||||
// Templates that convers a T to a string
|
||||
template <typename T>
|
||||
struct TypeName
|
||||
{
|
||||
static const char* Get() { return typeid(T).name(); }
|
||||
};
|
||||
template <> struct TypeName<int> { static const char* Get() { return "int"; } };
|
||||
template <> struct TypeName<QString> { static const char* Get() { return "QString"; } };
|
||||
template <> struct TypeName<bool> { static const char* Get() { return "bool"; } };
|
||||
template <> struct TypeName<QVariant> { static const char* Get() { return "QVariant"; } };
|
||||
|
||||
template<typename Test>
|
||||
bool ExecuteTest(int argc, char* argv[]) {
|
||||
Test test;
|
||||
return QTest::qExec(&test, argc, argv) == 0;
|
||||
}
|
||||
|
||||
template<typename Test>
|
||||
bool ExecuteGuiTest(int argc, char* argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
Test test;
|
||||
return QTest::qExec(&test, argc, argv) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test QGuiApplication
|
||||
*/
|
||||
class TestQGuiApplication : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void testExecution() {
|
||||
bool quit = false;
|
||||
dos_qguiapplication_create();
|
||||
QTimer::singleShot(100, [&quit](){ quit = true; dos_qguiapplication_quit(); });
|
||||
dos_qguiapplication_exec();
|
||||
QVERIFY(quit);
|
||||
dos_qguiapplication_delete();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Test QApplication
|
||||
*/
|
||||
class TestQApplication : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void testExecution() {
|
||||
bool quit = false;
|
||||
dos_qapplication_create();
|
||||
QTimer::singleShot(100, [&quit](){ quit = true; dos_qapplication_quit(); });
|
||||
dos_qapplication_exec();
|
||||
QVERIFY(quit);
|
||||
dos_qapplication_delete();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Test QQmlApplicationEngine
|
||||
*/
|
||||
class TestQQmlApplicationEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void initTestCase() {
|
||||
m_engine = nullptr;
|
||||
}
|
||||
|
||||
void init() {
|
||||
QVERIFY(m_engine == nullptr);
|
||||
dos_qqmlapplicationengine_create(&m_engine);
|
||||
QVERIFY(m_engine != nullptr);
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
dos_qqmlapplicationengine_delete(m_engine);
|
||||
m_engine = nullptr;
|
||||
}
|
||||
|
||||
void testCreateAndDelete() {
|
||||
|
||||
}
|
||||
|
||||
void testLoadUrl() {
|
||||
void* url = nullptr;
|
||||
dos_qurl_create(&url, "qrc:///main.qml", QUrl::TolerantMode);
|
||||
QVERIFY(url != nullptr);
|
||||
dos_qqmlapplicationengine_load_url(m_engine, url);
|
||||
dos_qurl_delete(url);
|
||||
}
|
||||
|
||||
void testRootObjects() {
|
||||
void** rootObjects = nullptr;
|
||||
int length = 0;
|
||||
void* url = nullptr;
|
||||
dos_qurl_create(&url, "qrc:///main.qml", QUrl::TolerantMode);
|
||||
dos_qqmlapplicationengine_load_url(m_engine, url);
|
||||
dos_qurl_delete(url);
|
||||
dos_qqmlapplicationengine_rootObjects(m_engine, &rootObjects, &length);
|
||||
QCOMPARE(length, 1);
|
||||
QObject* window = reinterpret_cast<QObject*>(rootObjects[0]);
|
||||
QVERIFY(window->isWindowType());
|
||||
dos_qobjectptr_array_delete(rootObjects);
|
||||
}
|
||||
|
||||
private:
|
||||
void* m_engine;
|
||||
};
|
||||
|
||||
/*
|
||||
* Test QQmlContext
|
||||
*/
|
||||
class TestQQmlContext {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Test DynamicQObject
|
||||
*/
|
||||
class TestDynamicQObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void memoryLeakTest() {
|
||||
DynamicQObject<QObject> dynamicQObject;
|
||||
BaseQObject baseQObject;
|
||||
}
|
||||
|
||||
void testRegisterSignal() {
|
||||
DynamicQObject<QObject> dynamicQObject;
|
||||
int index;
|
||||
dynamicQObject.registerSignal("fooSignal", {}, index);
|
||||
QVERIFY(index != -1);
|
||||
|
||||
QSignalSpy signalSpy(&dynamicQObject, SIGNAL(fooSignal()));
|
||||
dynamicQObject.emitSignal("fooSignal", {});
|
||||
QCOMPARE(signalSpy.count(), 1);
|
||||
}
|
||||
|
||||
void testSlotExecution() {
|
||||
testSlotExecutionForType<int>(10);
|
||||
testSlotExecutionForType<QString>("foo");
|
||||
testSlotExecutionForType<bool>(false);
|
||||
testSlotExecutionForType<QVariant>(QVariant(40));
|
||||
}
|
||||
|
||||
void testRegisterProperty() {
|
||||
DynamicQObject<QObject> dynamicQObject;
|
||||
int index = -1;
|
||||
bool result = false;
|
||||
result = dynamicQObject.registerSlot("foo", QMetaType::Int, {}, index);
|
||||
QVERIFY(index != -1);
|
||||
QVERIFY(result);
|
||||
result = dynamicQObject.registerSlot("setFoo", QMetaType::Void, {QMetaType::Int}, index);
|
||||
QVERIFY(index != -1);
|
||||
QVERIFY(result);
|
||||
result = dynamicQObject.registerSignal("fooChanged", {QMetaType::Int}, index);
|
||||
QVERIFY(index != -1);
|
||||
QVERIFY(result);
|
||||
result = dynamicQObject.registerProperty("foo", QMetaType::Int, "foo", "setFoo", "fooChanged");
|
||||
QVERIFY(result);;
|
||||
|
||||
int propertyValue = -1;
|
||||
|
||||
auto handler = [&propertyValue](const DynamicSlot &slot, const std::vector<QVariant> &args) -> QVariant {
|
||||
if (slot.name() == "foo")
|
||||
return propertyValue;
|
||||
else if (slot.name() == "setFoo")
|
||||
propertyValue = args.front().toInt();
|
||||
return QVariant();
|
||||
};
|
||||
|
||||
dynamicQObject.setOnSlotExecutedHandler(handler);
|
||||
|
||||
// Test property read
|
||||
QCOMPARE(dynamicQObject.property("foo").toInt(), -1);
|
||||
|
||||
// Test property write and signal emittion
|
||||
dynamicQObject.setProperty("foo", 10);
|
||||
QCOMPARE(propertyValue, 10);
|
||||
}
|
||||
|
||||
void benchmarkDynamicQObjectPerformance() {
|
||||
QBENCHMARK {
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
DynamicQObject<QObject> dynamicQObject;
|
||||
int index = -1;
|
||||
dynamicQObject.registerSlot("foo", QMetaType::Int, {}, index);
|
||||
dynamicQObject.registerSlot("setFoo", QMetaType::Void, {QMetaType::Int}, index);
|
||||
dynamicQObject.registerSignal("fooChanged", {QMetaType::Int}, index);
|
||||
dynamicQObject.registerProperty("foo", QMetaType::Int, "foo", "setFoo", "fooChanged");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void benchmarkDynamicQObject2Performance() {
|
||||
|
||||
QBENCHMARK {
|
||||
|
@ -106,7 +136,7 @@ private slots:
|
|||
{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<DOS::DynamicQObject2> dynamicQObject(factory.create([](const QString&, const std::vector<QVariant>&)-> QVariant{}));
|
||||
std::unique_ptr<DOS::DynamicQObject> dynamicQObject(factory.create([](const QString&, const std::vector<QVariant>&)-> QVariant{}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,47 +145,25 @@ private slots:
|
|||
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<DOS::DynamicQObject2> dynamicQObject(factory.create([](const QString&, const std::vector<QVariant>&)-> QVariant{}));
|
||||
std::unique_ptr<DOS::DynamicQObject> dynamicQObject(factory.create([](const QString&, const std::vector<QVariant>&)-> QVariant{}));
|
||||
QVERIFY(dynamicQObject != nullptr);
|
||||
|
||||
QSignalSpy signalSpy(dynamicQObject.get(), SIGNAL(fooChanged(int)));
|
||||
dynamicQObject->emitSignal("fooChanged", {10});
|
||||
QCOMPARE(signalSpy.count(), 1);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename ReturnType>
|
||||
void testSlotExecutionForType(ReturnType expectedReturnValue) {
|
||||
DynamicQObject<QObject> dynamicQObject;
|
||||
int index;
|
||||
auto type = static_cast<QMetaType::Type>(qMetaTypeId<ReturnType>());
|
||||
dynamicQObject.registerSlot("fooSlot", type, {type}, index);
|
||||
QVERIFY(index != -1);
|
||||
|
||||
// Call the slot and check return value
|
||||
bool called = false;
|
||||
bool obtainedArgument = false;
|
||||
bool argumentMatch = false;
|
||||
auto handler = [&called, expectedReturnValue, &obtainedArgument, &argumentMatch]
|
||||
(const DynamicSlot &, const std::vector<QVariant> &args) -> QVariant
|
||||
{
|
||||
obtainedArgument = args.size() == 1;
|
||||
argumentMatch = (!args.empty() && args.front().value<ReturnType>() == expectedReturnValue);
|
||||
called = true;
|
||||
return expectedReturnValue;
|
||||
};
|
||||
dynamicQObject.setOnSlotExecutedHandler(handler);
|
||||
ReturnType result;
|
||||
QMetaObject::invokeMethod(&dynamicQObject, "fooSlot",
|
||||
QGenericReturnArgument(TypeName<ReturnType>::Get(), &result),
|
||||
QGenericArgument(TypeName<ReturnType>::Get(), &expectedReturnValue));
|
||||
QVERIFY(called);
|
||||
QVERIFY(obtainedArgument);
|
||||
QVERIFY(argumentMatch);
|
||||
QCOMPARE(result, expectedReturnValue);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(TestDynamicQObject)
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
Q_INIT_RESOURCE(Resources);
|
||||
bool success = true;
|
||||
success &= ExecuteTest<TestQGuiApplication>(argc, argv);
|
||||
success &= ExecuteTest<TestQApplication>(argc, argv);
|
||||
success &= ExecuteGuiTest<TestQQmlApplicationEngine>(argc, argv);
|
||||
success &= ExecuteTest<TestDynamicQObject>(argc, argv);
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
#include "test_dynamicqobject.moc"
|
||||
|
|
Loading…
Reference in New Issue