Refactored the code for making the DynamicQObject testable and reusable

This commit is contained in:
cuke 2015-09-20 11:39:25 +02:00
parent 52a893577f
commit e73e37b587
15 changed files with 427 additions and 376 deletions

View File

@ -77,13 +77,13 @@ private:
}; };
BaseQAbstractListModel::BaseQAbstractListModel(void* modelObject, BaseQAbstractListModel::BaseQAbstractListModel(void* modelObject,
RowCountCallback rowCountCallback, RowCountCallback rowCountCallback,
ColumnCountCallback columnCountCallback, ColumnCountCallback columnCountCallback,
DataCallback dataCallback, DataCallback dataCallback,
SetDataCallback setDataCallback, SetDataCallback setDataCallback,
RoleNamesCallback roleNamesCallback, RoleNamesCallback roleNamesCallback,
FlagsCallback flagsCallback, FlagsCallback flagsCallback,
HeaderDataCallback headerDataCallback) HeaderDataCallback headerDataCallback)
: m_modelObject(modelObject) : m_modelObject(modelObject)
, m_rowCountCallback(rowCountCallback) , m_rowCountCallback(rowCountCallback)
, m_columnCountCallback(columnCountCallback) , m_columnCountCallback(columnCountCallback)
@ -192,8 +192,8 @@ void BaseQAbstractListModel::publicEndResetModel()
} }
void BaseQAbstractListModel::publicDataChanged(const QModelIndex& topLeft, void BaseQAbstractListModel::publicDataChanged(const QModelIndex& topLeft,
const QModelIndex& bottomRight, const QModelIndex& bottomRight,
const QVector<int>& roles) const QVector<int>& roles)
{ {
emit dataChanged(topLeft, bottomRight, roles); emit dataChanged(topLeft, bottomRight, roles);
} }

View File

@ -27,11 +27,21 @@ set(HEADERS_LIST
DOtherSide.h DOtherSide.h
DynamicQObject.h DynamicQObject.h
IDynamicQObject.h IDynamicQObject.h
OnSlotExecutedHandler.h
) )
set(SRC_LIST set(SRC_LIST
DOtherSide.cpp DOtherSide.cpp
OnSlotExecutedHandler.cpp
DynamicSlot.cpp
DynamicSignal.cpp
DynamicProperty.cpp
) )
# Shared version for distributing
add_library(${PROJECT_NAME} SHARED ${SRC_LIST} ${HEADERS_LIST}) add_library(${PROJECT_NAME} SHARED ${SRC_LIST} ${HEADERS_LIST})
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Qml Qt5::Quick) target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Qml Qt5::Quick)
# Static version for testing
add_library("${PROJECT_NAME}Static" STATIC ${SRC_LIST} ${HEADERS_LIST})
target_link_libraries("${PROJECT_NAME}Static" PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Qml Qt5::Quick)

View File

@ -15,6 +15,7 @@
#include "DynamicQObject.h" #include "DynamicQObject.h"
#include "BaseQAbstractListModel.h" #include "BaseQAbstractListModel.h"
#include "BaseQObject.h" #include "BaseQObject.h"
#include "OnSlotExecutedHandler.h"
void convert_to_cstring(const QString& source, char** destination) void convert_to_cstring(const QString& source, char** destination)
{ {
@ -298,8 +299,7 @@ void dos_qobject_create(void** vptr, void* dObjectPointer, DObjectCallback dObje
{ {
auto dynamicQObject = new BaseQObject(); auto dynamicQObject = new BaseQObject();
QQmlEngine::setObjectOwnership(dynamicQObject, QQmlEngine::CppOwnership); QQmlEngine::setObjectOwnership(dynamicQObject, QQmlEngine::CppOwnership);
dynamicQObject->setDObjectPointer(dObjectPointer); dynamicQObject->setOnSlotExecutedHandler(OnSlotExecutedHandler(dObjectPointer, dObjectCallback));
dynamicQObject->setDObjectCallback(dObjectCallback);
*vptr = dynamicQObject; *vptr = dynamicQObject;
} }
@ -428,7 +428,7 @@ void dos_qmodelindex_sibling(void* vptr, int row, int column, void* sibling)
} }
void dos_qabstractlistmodel_create(void** vptr, void dos_qabstractlistmodel_create(void** vptr,
void* modelObject, void* dObjectPointer,
DObjectCallback dObjectCallback, DObjectCallback dObjectCallback,
RowCountCallback rowCountCallback, RowCountCallback rowCountCallback,
ColumnCountCallback columnCountCallback, ColumnCountCallback columnCountCallback,
@ -438,7 +438,7 @@ void dos_qabstractlistmodel_create(void** vptr,
FlagsCallback flagsCallback, FlagsCallback flagsCallback,
HeaderDataCallback headerDataCallback) HeaderDataCallback headerDataCallback)
{ {
auto model = new BaseQAbstractListModel(modelObject, auto model = new BaseQAbstractListModel(dObjectPointer,
rowCountCallback, rowCountCallback,
columnCountCallback, columnCountCallback,
dataCallback, dataCallback,
@ -446,8 +446,7 @@ void dos_qabstractlistmodel_create(void** vptr,
roleNamesCallaback, roleNamesCallaback,
flagsCallback, flagsCallback,
headerDataCallback); headerDataCallback);
model->setDObjectPointer(modelObject); model->setOnSlotExecutedHandler(OnSlotExecutedHandler(dObjectPointer, dObjectCallback));
model->setDObjectCallback(dObjectCallback);
*vptr = model; *vptr = model;
} }

111
src/DynamicProperty.cpp Normal file
View File

@ -0,0 +1,111 @@
#include "DynamicProperty.h"
struct DynamicProperty::PropertyData final
{
PropertyData(const QString& name,
QMetaType::Type type,
const QString& readSlotName,
const QString& writeSlotName,
const QString& notifySignalName)
: name(name)
, type(type)
, readSlotName(readSlotName)
, writeSlotName(writeSlotName)
, notifySignalName(notifySignalName)
{}
PropertyData(const PropertyData& other)
: name(other.name)
, type(other.type)
, readSlotName(other.readSlotName)
, writeSlotName(other.writeSlotName)
, notifySignalName(other.notifySignalName)
{}
PropertyData& operator=(const PropertyData& other)
{
name = other.name;
type = other.type;
readSlotName = other.readSlotName;
writeSlotName = other.writeSlotName;
notifySignalName = other.notifySignalName;
return *this;
}
QString name;
QMetaType::Type type;
QString readSlotName;
QString writeSlotName;
QString notifySignalName;
};
DynamicProperty::DynamicProperty()
: d(nullptr)
{}
DynamicProperty::DynamicProperty(const QString& name, QMetaType::Type type, const QString& readSlotName, const QString& writeSlotName, const QString& notifySignalName)
: d(new PropertyData(name, type, readSlotName, writeSlotName, notifySignalName))
{
}
DynamicProperty::DynamicProperty(const DynamicProperty& other)
: d(nullptr)
{
if (other.d)
d.reset(new PropertyData(*other.d));
}
DynamicProperty& DynamicProperty::operator=(const DynamicProperty& other)
{
if (!other.d && d)
d.reset();
else if (other.d && !d)
d.reset(new PropertyData(*other.d));
else if (other.d && d)
*d = *other.d;
return *this;
}
DynamicProperty::~DynamicProperty()
{}
QString DynamicProperty::name() const
{
return d->name;
}
QMetaType::Type DynamicProperty::type() const
{
return d->type;
}
bool DynamicProperty::isReadable() const
{
return !d->readSlotName.isEmpty();
}
bool DynamicProperty::isWriteable() const
{
return !d->writeSlotName.isEmpty();
}
bool DynamicProperty::hasNotifySignal() const
{
return !d->notifySignalName.isEmpty();
}
QString DynamicProperty::readSlot() const
{
return d->readSlotName;
}
QString DynamicProperty::writeSlot() const
{
return d->writeSlotName;
}
QString DynamicProperty::notifySignal() const
{
return d->notifySignalName;
}

View File

@ -4,8 +4,6 @@
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QMetaType> #include <QtCore/QMetaType>
struct PropertyData;
class DynamicProperty final class DynamicProperty final
{ {
public: public:
@ -35,115 +33,6 @@ public:
QString notifySignal() const; QString notifySignal() const;
private: private:
struct PropertyData;
std::unique_ptr<PropertyData> d; std::unique_ptr<PropertyData> d;
}; };
struct PropertyData final
{
PropertyData(const QString& name,
QMetaType::Type type,
const QString& readSlotName,
const QString& writeSlotName,
const QString& notifySignalName)
: name(name)
, type(type)
, readSlotName(readSlotName)
, writeSlotName(writeSlotName)
, notifySignalName(notifySignalName)
{}
PropertyData(const PropertyData& other)
: name(other.name)
, type(other.type)
, readSlotName(other.readSlotName)
, writeSlotName(other.writeSlotName)
, notifySignalName(other.notifySignalName)
{}
PropertyData& operator=(const PropertyData& other)
{
name = other.name;
type = other.type;
readSlotName = other.readSlotName;
writeSlotName = other.writeSlotName;
notifySignalName = other.notifySignalName;
return *this;
}
QString name;
QMetaType::Type type;
QString readSlotName;
QString writeSlotName;
QString notifySignalName;
};
DynamicProperty::DynamicProperty()
: d(nullptr)
{}
DynamicProperty::DynamicProperty(const QString& name, QMetaType::Type type, const QString& readSlotName, const QString& writeSlotName, const QString& notifySignalName)
: d(new PropertyData(name, type, readSlotName, writeSlotName, notifySignalName))
{
}
DynamicProperty::DynamicProperty(const DynamicProperty& other)
: d(nullptr)
{
if (other.d)
d.reset(new PropertyData(*other.d));
}
DynamicProperty& DynamicProperty::operator=(const DynamicProperty& other)
{
if (!other.d && d)
d.reset();
else if (other.d && !d)
d.reset(new PropertyData(*other.d));
else if (other.d && d)
*d = *other.d;
return *this;
}
DynamicProperty::~DynamicProperty()
{}
QString DynamicProperty::name() const
{
return d->name;
}
QMetaType::Type DynamicProperty::type() const
{
return d->type;
}
bool DynamicProperty::isReadable() const
{
return !d->readSlotName.isEmpty();
}
bool DynamicProperty::isWriteable() const
{
return !d->writeSlotName.isEmpty();
}
bool DynamicProperty::hasNotifySignal() const
{
return !d->notifySignalName.isEmpty();
}
QString DynamicProperty::readSlot() const
{
return d->readSlotName;
}
QString DynamicProperty::writeSlot() const
{
return d->writeSlotName;
}
QString DynamicProperty::notifySignal() const
{
return d->notifySignalName;
}

View File

@ -17,6 +17,7 @@ template <class T>
class DynamicQObject : public T, public IDynamicQObject class DynamicQObject : public T, public IDynamicQObject
{ {
using SafeQMetaObjectPtr = std::unique_ptr<QMetaObject, void(*)(void*)>; using SafeQMetaObjectPtr = std::unique_ptr<QMetaObject, void(*)(void*)>;
using OnSlotExecutedHandler = std::function<QVariant(const DynamicSlot&, const std::vector<QVariant>&)>;
public: public:
/// Constructor /// Constructor
@ -25,11 +26,8 @@ public:
/// Destructor /// Destructor
virtual ~DynamicQObject(); virtual ~DynamicQObject();
/// Sets the function to be called from C++ to D or Nimrod /// Sets the on slot executed handler
void setDObjectCallback(IDynamicQObject::Callback callback) override; void setOnSlotExecutedHandler(const OnSlotExecutedHandler& handler);
/// Sets the D or Nimrod object that owns this DynamicQObject
void setDObjectPointer(void* dObjectPointer) override;
/// Register a new signal /// Register a new signal
bool registerSignal(const QString& name, bool registerSignal(const QString& name,
@ -61,8 +59,6 @@ public:
private: private:
bool executeSlot(const DynamicSlot& slot, void** args); bool executeSlot(const DynamicSlot& slot, void** args);
QVariant executeSlot(const DynamicSlot& slot, const std::vector<QVariant> &args);
bool readProperty(const DynamicProperty& property, void** args); bool readProperty(const DynamicProperty& property, void** args);
bool writeProperty(const DynamicProperty& property, void** args); bool writeProperty(const DynamicProperty& property, void** args);
@ -81,16 +77,13 @@ private:
QHash<QString, DynamicSlot> m_slotsByName; QHash<QString, DynamicSlot> m_slotsByName;
QHash<QByteArray, DynamicSlot> m_slotsBySignature; QHash<QByteArray, DynamicSlot> m_slotsBySignature;
QHash<QByteArray, DynamicProperty> m_propertiesByName; QHash<QByteArray, DynamicProperty> m_propertiesByName;
void* m_dObjectPointer; OnSlotExecutedHandler m_onSlotExecutedHandler;
IDynamicQObject::Callback m_dObjectCallback;
}; };
template <typename T> template <typename T>
DynamicQObject<T>::DynamicQObject() DynamicQObject<T>::DynamicQObject()
: T() : T()
, m_metaObject(nullptr, ::free) , m_metaObject(nullptr, ::free)
, m_dObjectPointer(nullptr)
, m_dObjectCallback(nullptr)
{ {
QMetaObjectBuilder builder; QMetaObjectBuilder builder;
builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
@ -100,15 +93,9 @@ DynamicQObject<T>::DynamicQObject()
} }
template <typename T> template <typename T>
void DynamicQObject<T>::setDObjectCallback(IDynamicQObject::Callback callback) void DynamicQObject<T>::setOnSlotExecutedHandler(const DynamicQObject::OnSlotExecutedHandler &handler)
{ {
m_dObjectCallback = callback; m_onSlotExecutedHandler = handler;
}
template <typename T>
void DynamicQObject<T>::setDObjectPointer(void* dObjectPointer)
{
m_dObjectPointer = dObjectPointer;
} }
template <typename T> template <typename T>
@ -284,15 +271,12 @@ bool DynamicQObject<T>::executeSlot(const DynamicSlot& slot, void** args)
if (!slot.isValid()) if (!slot.isValid())
return false; return false;
if (!m_dObjectCallback || !m_dObjectPointer)
return false;
std::vector<QVariant> arguments; std::vector<QVariant> arguments;
arguments.reserve(slot.argumentsTypes().size()); arguments.reserve(slot.argumentsTypes().size());
for (int i = 0; i < slot.argumentsTypes().count(); ++i) for (int i = 0; i < slot.argumentsTypes().count(); ++i)
arguments.emplace_back(QVariant(slot.argumentTypeAt(i), args[i + 1])); arguments.emplace_back(QVariant(slot.argumentTypeAt(i), args[i + 1]));
QVariant result = executeSlot(slot, arguments); QVariant result = m_onSlotExecutedHandler(slot, arguments);
if (slot.returnType() != QMetaType::Void && result.isValid()) if (slot.returnType() != QMetaType::Void && result.isValid())
{ {
@ -303,30 +287,6 @@ bool DynamicQObject<T>::executeSlot(const DynamicSlot& slot, void** args)
return true; return true;
} }
template <typename T>
QVariant DynamicQObject<T>::executeSlot(const DynamicSlot& slot, const std::vector<QVariant>& args)
{
QVariant result;
if (!m_dObjectCallback || !m_dObjectPointer)
return result;
// prepare slot name
QVariant slotName(slot.name());
// prepare void* for the QVariants
std::vector<void*> argumentsAsVoidPointers;
argumentsAsVoidPointers.reserve(args.size() + 1);
argumentsAsVoidPointers.emplace_back(&result);
for (size_t i = 0; i < args.size(); ++i)
argumentsAsVoidPointers.emplace_back((void*)(&args[i]));
// send them to the binding handler
m_dObjectCallback(m_dObjectPointer, &slotName, argumentsAsVoidPointers.size(), &argumentsAsVoidPointers[0]);
return result;
}
template <typename T> template <typename T>
bool DynamicQObject<T>::readProperty(const DynamicProperty& property, void** args) bool DynamicQObject<T>::readProperty(const DynamicProperty& property, void** args)
{ {
@ -370,8 +330,10 @@ bool DynamicQObject<T>::writeProperty(const DynamicProperty& property, void** ar
if (writeSlot.returnType() != QMetaType::Void) if (writeSlot.returnType() != QMetaType::Void)
return false; return false;
QVariant newValue(writeSlot.argumentTypeAt(0), args[0]); void* sortedArgs[2]; // The write property is peculiar because it has
executeSlot(writeSlot, {newValue}); 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; return true;
} }

89
src/DynamicSignal.cpp Normal file
View File

@ -0,0 +1,89 @@
#include "DynamicSignal.h"
struct DynamicSignal::SignalData
{
QString name;
QList<QMetaType::Type> argumentsTypes;
QByteArray signature;
};
DynamicSignal::DynamicSignal()
: d(nullptr)
{
}
DynamicSignal::DynamicSignal(const QString& name, const QList<QMetaType::Type>& arguments)
: d(new SignalData())
{
d->name = name;
d->signature = QByteArray();
d->argumentsTypes = arguments;
_initSignature();
}
DynamicSignal::DynamicSignal(const DynamicSignal& signal)
{
if (signal.isValid())
{
d.reset(new SignalData());
*d = *signal.d;
}
else
d.reset(nullptr);
}
DynamicSignal& DynamicSignal::operator=(const DynamicSignal& signal)
{
if (signal.isValid())
{
d.reset(new SignalData());
*d = *signal.d;
}
else
d.reset(nullptr);
return *this;
}
DynamicSignal::~DynamicSignal()
{
}
bool DynamicSignal::isValid() const
{
return d != nullptr;
}
QString DynamicSignal::name() const
{
return isValid() ? d->name : QString();
}
QByteArray DynamicSignal::signature() const
{
return isValid() ? d->signature : QByteArray();
}
bool DynamicSignal::validate(const QVariantList& arguments)
{
// TODO: here we should test if the given arguments
// match this signal signature
// This is important because a class could have multiple
// signals with the same name but different arguments
Q_UNUSED(arguments);
return true;
}
void DynamicSignal::_initSignature()
{
QString signature("%1(%2)");
QString arguments;
for (int i = 0; i < d->argumentsTypes.size(); ++i)
{
if (i != 0)
arguments += ',';
arguments += QMetaType::typeName(d->argumentsTypes[i]);
}
d->signature = signature.arg(d->name, arguments).toUtf8();
}

View File

@ -7,8 +7,6 @@
#include <QtCore/QMetaType> #include <QtCore/QMetaType>
#include <QtCore/QList> #include <QtCore/QList>
struct SignalData;
class DynamicSignal final class DynamicSignal final
{ {
public: public:
@ -28,93 +26,6 @@ public:
private: private:
void _initSignature(); void _initSignature();
struct SignalData;
std::unique_ptr<SignalData> d; std::unique_ptr<SignalData> d;
}; };
struct SignalData
{
QString name;
QList<QMetaType::Type> argumentsTypes;
QByteArray signature;
};
DynamicSignal::DynamicSignal()
: d(nullptr)
{
}
DynamicSignal::DynamicSignal(const QString& name, const QList<QMetaType::Type>& arguments)
: d(new SignalData())
{
d->name = name;
d->signature = QByteArray();
d->argumentsTypes = arguments;
_initSignature();
}
DynamicSignal::DynamicSignal(const DynamicSignal& signal)
{
if (signal.isValid())
{
d.reset(new SignalData());
*d = *signal.d;
}
else
d.reset(nullptr);
}
DynamicSignal& DynamicSignal::operator=(const DynamicSignal& signal)
{
if (signal.isValid())
{
d.reset(new SignalData());
*d = *signal.d;
}
else
d.reset(nullptr);
return *this;
}
DynamicSignal::~DynamicSignal()
{
}
bool DynamicSignal::isValid() const
{
return d != nullptr;
}
QString DynamicSignal::name() const
{
return isValid() ? d->name : QString();
}
QByteArray DynamicSignal::signature() const
{
return isValid() ? d->signature : QByteArray();
}
bool DynamicSignal::validate(const QVariantList& arguments)
{
// TODO: here we should test if the given arguments
// match this signal signature
// This is important because a class could have multiple
// signals with the same name but different arguments
Q_UNUSED(arguments);
return true;
}
void DynamicSignal::_initSignature()
{
QString signature("%1(%2)");
QString arguments;
for (int i = 0; i < d->argumentsTypes.size(); ++i)
{
if (i != 0)
arguments += ',';
arguments += QMetaType::typeName(d->argumentsTypes[i]);
}
d->signature = signature.arg(d->name, arguments).toUtf8();
}

96
src/DynamicSlot.cpp Normal file
View File

@ -0,0 +1,96 @@
#include "DynamicSlot.h"
struct DynamicSlot::SlotData
{
QString name;
QMetaType::Type returnType;
QList<QMetaType::Type> argumentsTypes;
QByteArray signature;
};
DynamicSlot::DynamicSlot()
: d(nullptr)
{}
DynamicSlot::DynamicSlot(const QString& name,
QMetaType::Type returnType,
const QList<QMetaType::Type>& argumentsTypes)
: d(new SlotData())
{
d->name = name;
d->returnType = returnType;
d->argumentsTypes = argumentsTypes;
_initSignature();
}
DynamicSlot::DynamicSlot(const DynamicSlot& slot)
{
if (slot.isValid())
{
d.reset(new SlotData());
*d = *slot.d;
}
else
d.reset(nullptr);
}
DynamicSlot& DynamicSlot::operator=(const DynamicSlot& slot)
{
if (slot.isValid())
{
d.reset(new SlotData());
*d = *slot.d;
}
else
d.reset(nullptr);
return *this;
}
DynamicSlot::~DynamicSlot()
{
}
QString DynamicSlot::name() const
{
return isValid() ? d->name : QString();
}
bool DynamicSlot::isValid() const
{
return d != nullptr;
}
QByteArray DynamicSlot::signature() const
{
return isValid() ? d->signature : QByteArray();
}
QMetaType::Type DynamicSlot::returnType() const
{
return isValid() ? d->returnType : QMetaType::UnknownType;
}
QList<QMetaType::Type> DynamicSlot::argumentsTypes() const
{
return isValid() ? d->argumentsTypes : QList<QMetaType::Type>();
}
QMetaType::Type DynamicSlot::argumentTypeAt(int i) const
{
return isValid() ? d->argumentsTypes.at(i) : QMetaType::UnknownType;
}
void DynamicSlot::_initSignature()
{
QString signature("%1(%2)");
QString arguments = "";
for (int i = 0; i < d->argumentsTypes.size(); ++i)
{
if (i != 0)
arguments += ',';
arguments += QMetaType::typeName(d->argumentsTypes[i]);
}
d->signature = signature.arg(d->name, arguments).toUtf8();
}

View File

@ -13,7 +13,6 @@
#include "private/qmetaobjectbuilder_p.h" #include "private/qmetaobjectbuilder_p.h"
struct SlotData;
class QString; class QString;
class DynamicSlot final class DynamicSlot final
@ -37,102 +36,6 @@ public:
private: private:
void _initSignature(); void _initSignature();
struct SlotData;
std::unique_ptr<SlotData> d; std::unique_ptr<SlotData> d;
}; };
struct SlotData
{
QString name;
QMetaType::Type returnType;
QList<QMetaType::Type> argumentsTypes;
QByteArray signature;
};
DynamicSlot::DynamicSlot()
: d(nullptr)
{
}
DynamicSlot::DynamicSlot(const QString& name,
QMetaType::Type returnType,
const QList<QMetaType::Type>& argumentsTypes)
: d(new SlotData())
{
d->name = name;
d->returnType = returnType;
d->argumentsTypes = argumentsTypes;
_initSignature();
}
DynamicSlot::DynamicSlot(const DynamicSlot& slot)
{
if (slot.isValid())
{
d.reset(new SlotData());
*d = *slot.d;
}
else
d.reset(nullptr);
}
DynamicSlot& DynamicSlot::operator=(const DynamicSlot& slot)
{
if (slot.isValid())
{
d.reset(new SlotData());
*d = *slot.d;
}
else
d.reset(nullptr);
return *this;
}
DynamicSlot::~DynamicSlot()
{
}
QString DynamicSlot::name() const
{
return isValid() ? d->name : QString();
}
bool DynamicSlot::isValid() const
{
return d != nullptr;
}
QByteArray DynamicSlot::signature() const
{
return isValid() ? d->signature : QByteArray();
}
QMetaType::Type DynamicSlot::returnType() const
{
return isValid() ? d->returnType : QMetaType::UnknownType;
}
QList<QMetaType::Type> DynamicSlot::argumentsTypes() const
{
return isValid() ? d->argumentsTypes : QList<QMetaType::Type>();
}
QMetaType::Type DynamicSlot::argumentTypeAt(int i) const
{
return isValid() ? d->argumentsTypes.at(i) : QMetaType::UnknownType;
}
void DynamicSlot::_initSignature()
{
QString signature("%1(%2)");
QString arguments = "";
for (int i = 0; i < d->argumentsTypes.size(); ++i)
{
if (i != 0)
arguments += ',';
arguments += QMetaType::typeName(d->argumentsTypes[i]);
}
d->signature = signature.arg(d->name, arguments).toUtf8();
}

View File

@ -8,12 +8,6 @@ public:
/// Destructor /// Destructor
virtual ~IDynamicQObject() = default; virtual ~IDynamicQObject() = default;
/// Sets the function to be called from C++ to D or Nimrod
virtual void setDObjectCallback(Callback callback) = 0;
/// Sets the D or Nimrod object that owns this DynamicQObject
virtual void setDObjectPointer(void* dObjectPointer) = 0;
/// Register a new signal /// Register a new signal
virtual bool registerSignal(const QString& name, virtual bool registerSignal(const QString& name,
const QList<QMetaType::Type>& argumentsTypes, const QList<QMetaType::Type>& argumentsTypes,

View File

@ -0,0 +1,31 @@
#include "OnSlotExecutedHandler.h"
#include "DynamicSlot.h"
OnSlotExecutedHandler::OnSlotExecutedHandler(void *dObjectPointer,
IDynamicQObject::Callback dObjectCallback)
: m_dObjectPointer(dObjectPointer)
, m_dObjectCallback(dObjectCallback)
{}
QVariant OnSlotExecutedHandler::operator()(const DynamicSlot& slot, const std::vector<QVariant> &args)
{
QVariant result;
if (!m_dObjectCallback || !m_dObjectPointer)
return result;
// prepare slot name
QVariant slotName(slot.name());
// prepare void* for the QVariants
std::vector<void*> argumentsAsVoidPointers;
argumentsAsVoidPointers.reserve(args.size() + 1);
argumentsAsVoidPointers.emplace_back(&result);
for (size_t i = 0; i < args.size(); ++i)
argumentsAsVoidPointers.emplace_back((void*)(&args[i]));
// send them to the binding handler
m_dObjectCallback(m_dObjectPointer, &slotName, argumentsAsVoidPointers.size(), &argumentsAsVoidPointers[0]);
return result;
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <QVariant>
#include <vector>
#include "IDynamicQObject.h"
class DynamicSlot;
class OnSlotExecutedHandler
{
public:
OnSlotExecutedHandler(void* dObjectPointer, IDynamicQObject::Callback dObjectCallback);
QVariant operator()(const DynamicSlot& slot, std::vector<QVariant> const& args);
private:
void* m_dObjectPointer;
IDynamicQObject::Callback m_dObjectCallback;
};

View File

@ -11,4 +11,4 @@ find_package(Qt5Test REQUIRED)
add_executable(${PROJECT_NAME} test_dynamicqobject.cpp) add_executable(${PROJECT_NAME} test_dynamicqobject.cpp)
target_link_libraries(${PROJECT_NAME} Qt5::Test) target_link_libraries(${PROJECT_NAME} DOtherSideStatic Qt5::Test)

View File

@ -1,7 +1,22 @@
#include <QTest> #include <QTest>
#include <QSignalSpy> #include <QSignalSpy>
#include <tuple>
#include <iostream>
#include <QDebug>
#include "../src/BaseQObject.h"
#include "../src/DynamicQObject.h" #include "../src/DynamicQObject.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"; } };
class TestDynamicQObject : public QObject class TestDynamicQObject : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -9,6 +24,7 @@ class TestDynamicQObject : public QObject
private slots: private slots:
void memoryLeakTest() { void memoryLeakTest() {
DynamicQObject<QObject> dynamicQObject; DynamicQObject<QObject> dynamicQObject;
BaseQObject baseQObject;
} }
void testRegisterSignal() { void testRegisterSignal() {
@ -22,11 +38,11 @@ private slots:
QCOMPARE(signalSpy.count(), 1); QCOMPARE(signalSpy.count(), 1);
} }
void testRegisterSlot() { void testSlotExecution() {
DynamicQObject<QObject> dynamicQObject; testSlotExecutionForType<int>(10);
int index; testSlotExecutionForType<QString>("foo");
dynamicQObject.registerSlot("fooSlot", QMetaType::Void, {}, index); testSlotExecutionForType<bool>(false);
QCOMPARE(index != -1, true); testSlotExecutionForType<QVariant>(QVariant(40));
} }
void testRegisterProperty() { void testRegisterProperty() {
@ -45,6 +61,27 @@ private slots:
result = dynamicQObject.registerProperty("foo", QMetaType::Int, "foo", "setFoo", "fooChanged"); result = dynamicQObject.registerProperty("foo", QMetaType::Int, "foo", "setFoo", "fooChanged");
QCOMPARE(result, true); QCOMPARE(result, true);
} }
private:
template<typename ReturnType>
void testSlotExecutionForType(ReturnType expectedReturnValue) {
DynamicQObject<QObject> dynamicQObject;
int index;
dynamicQObject.registerSlot("fooSlot", (QMetaType::Type)qMetaTypeId<ReturnType>(), {}, index);
QCOMPARE(index != -1, true);
// Call the slot and check return value
bool called = false;
auto handler = [&called, expectedReturnValue](const DynamicSlot &slot, const std::vector<QVariant> &args) -> QVariant {
called = true;
return expectedReturnValue;
};
dynamicQObject.setOnSlotExecutedHandler(handler);
ReturnType result;
QMetaObject::invokeMethod(&dynamicQObject, "fooSlot", QReturnArgument<ReturnType>(TypeName<ReturnType>::Get(), result));
QCOMPARE(called, true);
QCOMPARE(result, expectedReturnValue);
}
}; };
QTEST_MAIN(TestDynamicQObject) QTEST_MAIN(TestDynamicQObject)