Temporary removed QAbstractListModel support and improved unit tests

This commit is contained in:
Filippo Cucchetto 2015-12-28 13:21:02 +01:00
parent 711131b4f4
commit 8f188552c2
18 changed files with 229 additions and 839 deletions

View File

@ -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
)

View File

@ -1,5 +0,0 @@
#pragma once
#include "DOtherSide/DynamicQObject.h"
using BaseQObject = DynamicQObject<QObject>;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
}
}

View File

@ -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>();

View File

@ -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

View File

@ -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()) {

View File

@ -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

View File

@ -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);
}

View File

@ -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)

6
test/Resources.qrc Normal file
View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>CMakeLists.txt</file>
<file>main.qml</file>
</qresource>
</RCC>

8
test/main.qml Normal file
View File

@ -0,0 +1,8 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
width: 100
height: 100
Component.onCompleted: visible = true
}

View File

@ -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"