Browse Source

implemented basic DI for service classes

pull/2/head
Skycoder42 7 years ago
parent
commit
59ccb7fa96
  1. 2
      examples/mvvmcore/SampleCore/echoservice.h
  2. 5
      examples/mvvmwidgets/SampleWidgets/main.cpp
  3. 12
      examples/mvvmwidgets/SampleWidgets/widgetseventservice.cpp
  4. 4
      examples/mvvmwidgets/SampleWidgets/widgetseventservice.h
  5. 6
      src/mvvmcore/mvvmcore.pro
  6. 12
      src/mvvmcore/qtmvvmcore_global.cpp
  7. 5
      src/mvvmcore/qtmvvmcore_helpertypes.h
  8. 19
      src/mvvmcore/qtmvvmcore_logging_p.h
  9. 156
      src/mvvmcore/serviceregistry.cpp
  10. 57
      src/mvvmcore/serviceregistry.h
  11. 3
      src/mvvmcore/serviceregistry_p.h

2
examples/mvvmcore/SampleCore/echoservice.h

@ -8,7 +8,7 @@ class EchoService : public QObject
Q_OBJECT
public:
explicit EchoService(QObject *parent = nullptr);
Q_INVOKABLE explicit EchoService(QObject *parent = nullptr);
public Q_SLOTS:
void ping(const QString &message);

5
examples/mvvmwidgets/SampleWidgets/main.cpp

@ -30,7 +30,10 @@ int main(int argc, char *argv[])
return new WidgetsEventService(echo, nullptr);
});
if(TEST_CURRENT == TEST_INST)
QtMvvm::ServiceRegistry::instance()->registerInterface<IEventService>(new WidgetsEventService());
QtMvvm::ServiceRegistry::instance()->registerInterface<IEventService>(new WidgetsEventService(QtMvvm::ServiceRegistry::instance()->acquireInstance<EchoService>()));
//debug test
auto event = QtMvvm::ServiceRegistry::instance()->acquireInstance<IEventService>();
return a.exec();
}

12
examples/mvvmwidgets/SampleWidgets/widgetseventservice.cpp

@ -14,7 +14,9 @@ WidgetsEventService::WidgetsEventService(EchoService *svc, QObject *parent) :
_cnt(0),
_events(),
_echoService(svc)
{}
{
qtmvvm_init();
}
int WidgetsEventService::addEvent(const QString &name)
{
@ -36,3 +38,11 @@ void WidgetsEventService::removeEvent(int eventId)
{
_events.remove(eventId);
}
void WidgetsEventService::qtmvvm_init()
{
qDebug(Q_FUNC_INFO);
Q_ASSERT(_echoService);
connect(_echoService, &EchoService::pong,
this, &WidgetsEventService::eventTriggered);
}

4
examples/mvvmwidgets/SampleWidgets/widgetseventservice.h

@ -18,7 +18,7 @@ class WidgetsEventService : public QObject, public IEventService
QTMVVM_INJECT_PROP(EchoService*, echoService, _echoService)
public:
explicit WidgetsEventService(QObject *parent = nullptr);
Q_INVOKABLE explicit WidgetsEventService(QObject *parent = nullptr);
explicit WidgetsEventService(EchoService* svc, QObject *parent = nullptr);
int addEvent(const QString &name) override;
@ -31,6 +31,8 @@ private:
int _cnt;
QHash<int, QSharedPointer<QTimer>> _events;
EchoService* _echoService;
Q_INVOKABLE void qtmvvm_init();
};
#endif // WIDGETSEVENTSERVICE_H

6
src/mvvmcore/mvvmcore.pro

@ -10,12 +10,14 @@ HEADERS += \
coreapp_p.h \
serviceregistry.h \
serviceregistry_p.h \
qtmvvmcore_helpertypes.h
qtmvvmcore_helpertypes.h \
qtmvvmcore_logging_p.h
SOURCES += \
viewmodel.cpp \
coreapp.cpp \
serviceregistry.cpp
serviceregistry.cpp \
qtmvvmcore_global.cpp
TRANSLATIONS += \
translations/qtmvvmcore_de.ts \

12
src/mvvmcore/qtmvvmcore_global.cpp

@ -0,0 +1,12 @@
#include "qtmvvmcore_global.h"
#include "qtmvvmcore_logging_p.h"
namespace QtMvvm {
#ifdef QT_NO_DEBUG
Q_LOGGING_CATEGORY(mvvmcore, "qtmvvm", QtInfoMsg)
#else
Q_LOGGING_CATEGORY(mvvmcore, "qtmvvm", QtDebugMsg)
#endif
}

5
src/mvvmcore/qtmvvmcore_helpertypes.h

@ -43,6 +43,11 @@ inline QByteArray inject_iid() {
return qobject_iid<TInjectPtr>();
}
template <typename TFunc, typename T1, typename... TArgs>
inline std::function<QObject*(QObjectList)> pack_function_imp(const TFunc &fn, QByteArrayList &injectables);
template <typename TFunc>
inline std::function<QObject*(QObjectList)> pack_function_imp(const TFunc &fn, QByteArrayList &injectables);
template <typename TFunc, typename T1, typename... TArgs>
inline std::function<QObject*(QObjectList)> pack_function_imp(const TFunc &fn, QByteArrayList &injectables) {
injectables.append(inject_iid<T1>());

19
src/mvvmcore/qtmvvmcore_logging_p.h

@ -0,0 +1,19 @@
#ifndef LOGGING_P_H
#define LOGGING_P_H
#include <QtCore/QLoggingCategory>
#include "qtmvvmcore_global.h"
namespace QtMvvm {
Q_DECLARE_LOGGING_CATEGORY(mvvmcore)
}
#define logDebug(...) qCDebug(mvvmcore, __VA_ARGS__)
#define logInfo(...) qCInfo(mvvmcore, __VA_ARGS__)
#define logWarning(...) qCWarning(mvvmcore, __VA_ARGS__)
#define logCritical(...) qCCritical(mvvmcore, __VA_ARGS__)
#endif // LOGGING_P_H

156
src/mvvmcore/serviceregistry.cpp

@ -1,5 +1,6 @@
#include "serviceregistry.h"
#include "serviceregistry_p.h"
#include "qtmvvmcore_logging_p.h"
#include <QtCore/QGlobalStatic>
#include <QtCore/QRegularExpression>
@ -22,17 +23,29 @@ ServiceRegistry *ServiceRegistry::instance()
void ServiceRegistry::registerService(const QByteArray &iid, const QMetaObject *metaObject)
{
QMutexLocker _(&d->serviceMutex);
Q_ASSERT(!d->services.contains(iid)); //TODO exception
if(d->services.contains(iid))
throw ServiceExistsException(iid);
d->services.insert(iid, QSharedPointer<ServiceRegistryPrivate::MetaServiceInfo>::create(metaObject));
}
void ServiceRegistry::registerService(const QByteArray &iid, const std::function<QObject*(const QObjectList &)> &fn, QByteArrayList injectables)
{
QMutexLocker _(&d->serviceMutex);
Q_ASSERT(!d->services.contains(iid)); //TODO exception
if(d->services.contains(iid))
throw ServiceExistsException(iid);
d->services.insert(iid, QSharedPointer<ServiceRegistryPrivate::FnServiceInfo>::create(fn, injectables));
}
QObject *ServiceRegistry::acquireInstanceObject(const QByteArray &iid)
{
QMutexLocker _(&d->serviceMutex);
auto ref = d->services.value(iid);
if(ref)
return ref->instance(d.data(), iid);
else
throw ServiceDependencyException(iid);
}
// ------------- Private Implementation -------------
ServiceRegistryPrivate::ServiceRegistryPrivate() :
@ -52,10 +65,16 @@ ServiceRegistryPrivate::ServiceInfo::~ServiceInfo()
QMetaObject::invokeMethod(_instance, "deleteLater");
}
QObject *ServiceRegistryPrivate::ServiceInfo::instance(ServiceRegistryPrivate *d)
QObject *ServiceRegistryPrivate::ServiceInfo::instance(ServiceRegistryPrivate *d, const QByteArray &iid)
{
if(!_instance)
if(!_instance) {
logDebug() << "Constructing service of type" << iid;
_instance = construct(d);
if(!_instance)
throw ServiceConstructionException("Failed to construct service of type " +
iid +
" with unknown error");
}
return _instance;
}
@ -69,8 +88,12 @@ ServiceRegistryPrivate::FnServiceInfo::FnServiceInfo(const std::function<QObject
QObject *ServiceRegistryPrivate::FnServiceInfo::construct(ServiceRegistryPrivate *d) const
{
QObjectList params;
foreach(auto inj, injectables)
params.append(d->services.value(inj)->instance(d));//TODO exception
foreach(auto iid, injectables) {
auto ref = d->services.value(iid);
if(!ref)
throw ServiceDependencyException(iid);
params.append(ref->instance(d, iid));
}
return creator(params);
}
@ -83,26 +106,117 @@ ServiceRegistryPrivate::MetaServiceInfo::MetaServiceInfo(const QMetaObject *meta
QObject *ServiceRegistryPrivate::MetaServiceInfo::construct(ServiceRegistryPrivate *d) const
{
auto instance = metaObject->newInstance();
Q_ASSERT(instance); //TODO exception
if(!instance) {
throw ServiceConstructionException(QByteArrayLiteral("Failed to construct object of type ") +
metaObject->className() +
QByteArrayLiteral(" - make shure there is an invokable constructor of the format: Q_INVOKABLE MyClass(QObject*)"));
}
static QRegularExpression nameRegex(QStringLiteral(R"__(^__qtmvvm_inject_(.+)$)__"),
QRegularExpression::OptimizeOnFirstUsageOption);
for(auto i = 0; i < metaObject->propertyCount(); i++) {
auto prop = metaObject->property(i);
auto match = nameRegex.match(QString::fromUtf8(prop.name()));
if(match.hasMatch()) {
auto tPropIndex = metaObject->indexOfProperty(qUtf8Printable(match.captured(1)));
if(tPropIndex == -1)
continue; //TODO warning
auto tProp = metaObject->property(tPropIndex);
auto inject = prop.read(instance).toByteArray();
auto injObj = d->services.value(inject)->instance(d); //TODO exception
tProp.write(instance, QVariant::fromValue(injObj)); //TODO check if casting works
try {
for(auto i = 0; i < metaObject->propertyCount(); i++) {
auto prop = metaObject->property(i);
auto match = nameRegex.match(QString::fromUtf8(prop.name()));
if(match.hasMatch()) {
auto tPropIndex = metaObject->indexOfProperty(qUtf8Printable(match.captured(1)));
if(tPropIndex == -1) {
logWarning().noquote() << "Found hint to inject property"
<< match.captured(1)
<< "but no property of that name was found";
continue;
}
auto tProp = metaObject->property(tPropIndex);
auto iid = prop.read(instance).toByteArray();
auto ref = d->services.value(iid);
if(!ref)
throw ServiceDependencyException(iid);
auto injObj = ref->instance(d, iid);
tProp.write(instance, QVariant::fromValue(injObj)); //TODO check if casting works
}
}
auto initMethod = metaObject->indexOfMethod("qtmvvm_init()");
if(initMethod != -1) {
auto method = metaObject->method(initMethod);
method.invoke(instance);
}
return instance;
} catch (...) {
instance->deleteLater();
throw;
}
}
// ------------- Exceptions Implementation -------------
ServiceExistsException::ServiceExistsException(const QByteArray &iid) :
_msg("An interface with the type id \"" + iid + "\" has already been registered")
{}
ServiceExistsException::ServiceExistsException(const ServiceExistsException * const other) :
_msg(other->_msg)
{}
const char *ServiceExistsException::what() const noexcept
{
return _msg.constData();
}
void ServiceExistsException::raise() const
{
throw (*this);
}
QException *ServiceExistsException::clone() const
{
return new ServiceExistsException(this);
}
//TODO call "init" method
return instance;
ServiceConstructionException::ServiceConstructionException(const QByteArray &message) :
_msg(message)
{}
ServiceConstructionException::ServiceConstructionException(const ServiceConstructionException * const other) :
_msg(other->_msg)
{}
const char *ServiceConstructionException::what() const noexcept
{
return _msg.constData();
}
void ServiceConstructionException::raise() const
{
throw (*this);
}
QException *ServiceConstructionException::clone() const
{
return new ServiceConstructionException(this);
}
ServiceDependencyException::ServiceDependencyException(const QByteArray &iid) :
ServiceConstructionException("Failed to construct service because of missing dependency: " + iid)
{}
ServiceDependencyException::ServiceDependencyException(const ServiceDependencyException * const other) :
ServiceConstructionException(other)
{}
void ServiceDependencyException::raise() const
{
throw (*this);
}
QException *ServiceDependencyException::clone() const
{
return new ServiceDependencyException(this);
}

57
src/mvvmcore/serviceregistry.h

@ -5,6 +5,7 @@
#include <QtCore/qscopedpointer.h>
#include <QtCore/qvariant.h>
#include <QtCore/qexception.h>
#include "QtMvvmCore/qtmvvmcore_global.h"
@ -37,10 +38,60 @@ public:
const std::function<QObject*(const QObjectList &)> &fn,
QByteArrayList injectables);
template <typename TInterface>
TInterface *acquireInstance();
QObject *acquireInstanceObject(const QByteArray &iid);
private:
QScopedPointer<ServiceRegistryPrivate> d;
};
class Q_MVVMCORE_EXPORT ServiceExistsException : public QException
{
public:
ServiceExistsException(const QByteArray &iid);
const char *what() const noexcept override;
void raise() const override;
QException *clone() const override;
protected:
ServiceExistsException(const ServiceExistsException * const other);
const QByteArray _msg;
};
class Q_MVVMCORE_EXPORT ServiceConstructionException : public QException
{
public:
ServiceConstructionException(const QByteArray &message);
const char *what() const noexcept override;
void raise() const override;
QException *clone() const override;
protected:
ServiceConstructionException(const ServiceConstructionException * const other);
const QByteArray _msg;
};
class Q_MVVMCORE_EXPORT ServiceDependencyException : public ServiceConstructionException
{
public:
ServiceDependencyException(const QByteArray &iid);
void raise() const override;
QException *clone() const override;
protected:
ServiceDependencyException(const ServiceDependencyException * const other);
};
// ------------- Generic Implementation -------------
#define QTMVVM_SERVICE_ASSERT(tint, tsvc) \
static_assert(__helpertypes::is_valid_interface<TInterface, TService>::value, "TService must implement the given TInterface interface and be a qobject class"); \
Q_ASSERT_X(qobject_interface_iid<TInterface*>(), Q_FUNC_INFO, "TInterface must be registered with Q_DECLARE_INTERFACE");
@ -103,6 +154,12 @@ void ServiceRegistry::registerObject(TService *service)
#undef QTMVVM_SERVICE_ASSERT
template<typename TInterface>
TInterface *ServiceRegistry::acquireInstance()
{
return qobject_cast<TInterface*>(acquireInstanceObject(__helpertypes::inject_iid<TInterface*>()));
}
}
#endif // QTMVVM_SERVICEREGISTRY_H

3
src/mvvmcore/serviceregistry_p.h

@ -2,6 +2,7 @@
#define QTMVVM_SERVICEREGISTRY_P_H
#include <QtCore/QMutex>
#include <QtCore/QSharedPointer>
#include "qtmvvmcore_global.h"
#include "serviceregistry.h"
@ -16,7 +17,7 @@ public:
ServiceInfo();
virtual ~ServiceInfo();
QObject *instance(ServiceRegistryPrivate *d);
QObject *instance(ServiceRegistryPrivate *d, const QByteArray &iid);
protected:
virtual QObject *construct(ServiceRegistryPrivate *d) const = 0;

Loading…
Cancel
Save