Browse Source

extend service registry with inject and construct

also completed registry doc
pull/2/head
Skycoder42 7 years ago
parent
commit
a60dfd4377
No known key found for this signature in database GPG Key ID: 8E01AD9EF0578D2B
  1. 6
      doc/Doxyfile
  2. 62
      doc/injection.dox
  3. 418
      doc/serviceregistry.dox
  4. 2
      src/mvvmcore/coreapp.cpp
  5. 33
      src/mvvmcore/injection.h
  6. 3
      src/mvvmcore/mvvmcore.pro
  7. 23
      src/mvvmcore/qtmvvmcore_global.h
  8. 96
      src/mvvmcore/serviceregistry.cpp
  9. 48
      src/mvvmcore/serviceregistry.h
  10. 4
      src/mvvmcore/serviceregistry_p.h
  11. 1
      src/mvvmcore/settingssetup.h
  12. 1
      src/mvvmcore/viewmodel.h
  13. 3
      sync.profile

6
doc/Doxyfile

@ -925,7 +925,9 @@ EXCLUDE_PATTERNS = moc_* \
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS = QAbstractAspect \
EXCLUDE_SYMBOLS = QtMvvm::__helpertypes \
\
QAbstractAspect \
QBasicAtomicInteger \
QBasicAtomicPointer \
QBasicMutex \
@ -1352,7 +1354,7 @@ GENERATE_HTMLHELP = NO
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler (hhc.exe). If non-empty,

62
doc/injection.dox

@ -0,0 +1,62 @@
/*!
@def QTMVVM_INJECT
@param classType The type of the property to be injected
@param name The name of the property to be injected
This macro creates an additional property that is detected by the QtMvvm::ServiceRegistry and
contains the information needed to inject the property automatically. For more details on
the property injection, see QtMvvm::ServiceRegistry
Sample code for usage:
@code{.cpp}
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(IService* service READ service WRITE setService)
QTMVVM_INJECT(IService*, service)
public:
//...
}
@endcode
@note All properties that make use of interfaces must register the interfaces via
QtMvvm::registerInterfaceConverter
@sa #QTMVVM_INJECT_PROP, QtMvvm::registerInterfaceConverter, QtMvvm::ServiceRegistry
*/
/*!
@def QTMVVM_INJECT_PROP
@param type The type of the property to be created
@param name The name of the property to be created
@param member The name of the member variable to use for the property
This macro is a shortcut for #QTMVVM_INJECT to create a property and mark it for injection in
one step. The property is created by using a member variable. This means it will have no public
member functions to access the property, only the property accessors itself.
Sample code for usage:
@code{.cpp}
class MyClass : public QObject
{
Q_OBJECT
QTMVVM_INJECT_PROP(IService*, service, _service)
public:
//...
private:
IService* _service;
}
@endcode
@note All properties that make use of interfaces must register the interfaces via
QtMvvm::registerInterfaceConverter
@sa #QTMVVM_INJECT, QtMvvm::registerInterfaceConverter, QtMvvm::ServiceRegistry
*/

418
doc/serviceregistry.dox

@ -0,0 +1,418 @@
/*!
@class QtMvvm::ServiceRegistry
This is the class responsible for the dependency injection (DI) part. All services registered
with the registry are available for DI. When using lazy initialization, services themselves
can also have dependencies to be injected. When the registry creates them it will
automatically inject them. Automatic DI is also done for ViewModel created by the CoreApp.
The following example shows how to use DI. The first shows how to use it with a simple,
interface-less service for an already created object:
@code{.cpp}
// service.h
class Service : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE explicit Service(QObject *parent = nullptr); //required signature
public Q_SLOTS:
void baum();
};
// myclass.h
class MyClass : public QObject
{
Q_OBJECT
QTMVVM_INJECT_PROP(Service*, service, _service)
//...
private:
Service *_service;
};
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtMvvm::ServiceRegistry::instance()->registerObject<Service>();
//...
auto myObj = new MyClass(parent);
QtMvvm::ServiceRegistry::instance()->injectServices(myObj);
// _service is now initialized via a valid Service
}
@endcode
The second example shows how to use it for interface based services and on objects that are
created via the registry:
@code{.cpp}
// iservice.h
class IService
{
public:
virtual inline ~IService() = default;
public Q_SLOTS:
virtual void baum() = 0;
};
#define IServiceIid "com.example.myapp.IService"
Q_DECLARE_INTERFACE(IService, IServiceIid)
// service.h
class Service : public QObject, public IService
{
Q_OBJECT
Q_INTERFACES(IService)
public:
Q_INVOKABLE explicit Service(QObject *parent = nullptr); //required signature
public Q_SLOTS:
void baum() override;
};
// myclass.h
class MyClass : public QObject
{
Q_OBJECT
QTMVVM_INJECT_PROP(IService*, service, _service)
public:
Q_INVOKABLE MyClass(QObject *parent = nullptr); //required signature
//...
private:
IService *_service;
};
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtMvvm::registerInterfaceConverter<IService>();
QtMvvm::ServiceRegistry::instance()->registerInterface<IService, Service>();
//...
auto myObj = QtMvvm::ServiceRegistry::instance()->constructInjected<MyClass>();
// _service is now initialized via a valid IService
}
@endcode
@sa QtMvvm::registerInterfaceConverter, #QTMVVM_INJECT, #QTMVVM_INJECT_PROP, ViewModel
*/
/*!
@fn QtMvvm::ServiceRegistry::registerInterface(bool)
@tparam TInterface The interface type to register the service for
@tparam TService The service to be registered for that interface. Must extend QObject and
implement TInterface
@param weak Specifies if the registration should be a weak one or a normal one
@throws ServiceExistsException If a non-weak service has already been registered for
TInterface
If the function returns successfully, from now on a service of the given type can be accessed
via the registry for the interface. The service is lazy initialized an will be created as soon
as it is requested for the first time. If it has any injectable properties, they will be
automatically injected on construction.
If the service is registered as weak, registering another service for the same interface will
not throw an exception but instead discard (and delete) this one.
@attention Make shure to register TInterface via QtMvvm::registerInterfaceConverter, otherwise
injection for the interface is not possible. In addition to this, TService must have an
invokable constructor with the following signature:
`Q_INVOKABLE explicit TService(QObject *parent = nullptr);`
@sa ServiceRegistry::registerObject, QtMvvm::registerInterfaceConverter,
ServiceRegistry::registerService, ServiceRegistry::service
*/
/*!
@fn QtMvvm::ServiceRegistry::registerInterface(const TFunc &, bool)
@tparam TInterface The interface type to register the service for
@tparam TService The service to be registered for that interface. Must extend QObject and
implement TInterface
@tparam TFunc The function type of the `fn` parameter.
@param fn The function to be called to construct the service
@param weak Specifies if the registration should be a weak one or a normal one
@throws ServiceExistsException If a non-weak service has already been registered for
TInterface
If the function returns successfully, from now on a service of the given type can be accessed
via the registry for the interface. The service is lazy initialized an will be created as soon
as it is requested for the first time. It is created by calling the given `fn` function.
`fn` must have the following signature:
@code{.cpp}
TService *fn(...);
@endcode
TService is the template parameter of this function. The arguments are variable, but if
arguments are present, they all must be pointers of types that are injectable via the registry.
When the function is called internally, the registry will "inject" all services as parameters
of this function.
If the service is registered as weak, registering another service for the same interface will
not throw an exception but instead discard (and delete) this one.
@attention Make shure to register TInterface via QtMvvm::registerInterfaceConverter, otherwise
injection for the interface is not possible.
@sa ServiceRegistry::registerObject, QtMvvm::registerInterfaceConverter,
ServiceRegistry::registerService, ServiceRegistry::service
*/
/*!
@fn QtMvvm::ServiceRegistry::registerInterface(TService *, bool)
@tparam TInterface The interface type to register the service for
@tparam TService The service to be registered for that interface. Must extend QObject and
implement TInterface
@param service The service instance to be registered as service
@param weak Specifies if the registration should be a weak one or a normal one
@throws ServiceExistsException If a non-weak service has already been registered for
TInterface
If the function returns successfully, from now on a service of the given type can be accessed
via the registry for the interface. The service instance is from that point on owned by the
registry. No DI is performed on the passed service.
If the service is registered as weak, registering another service for the same interface will
not throw an exception but instead discard (and delete) this one.
@attention Make shure to register TInterface via QtMvvm::registerInterfaceConverter, otherwise
injection for the interface is not possible.
@sa ServiceRegistry::registerObject, QtMvvm::registerInterfaceConverter,
ServiceRegistry::registerService, ServiceRegistry::service
*/
/*!
@fn QtMvvm::ServiceRegistry::registerObject(bool)
@tparam TService The service to be registered by its own type. Must extend QObject
@param weak Specifies if the registration should be a weak one or a normal one
@throws ServiceExistsException If a non-weak service has already been registered for
TService
If the function returns successfully, from now on a service of the given type can be accessed
via the registry. The service is lazy initialized an will be created as soon
as it is requested for the first time. If it has any injectable properties, they will be
automatically injected on construction.
If the service is registered as weak, registering another service for the same TService will
not throw an exception but instead discard (and delete) this one.
@attention In order to be able to create the service, TService must have an invokable
constructor with the following signature:
`Q_INVOKABLE explicit TService(QObject *parent = nullptr);`
@sa ServiceRegistry::registerInterface, ServiceRegistry::registerService,
ServiceRegistry::service
*/
/*!
@fn QtMvvm::ServiceRegistry::registerObject(const TFunc &, bool)
@tparam TService The service to be registered by its own type. Must extend QObject
@tparam TFunc The function type of the `fn` parameter.
@param fn The function to be called to construct the service
@param weak Specifies if the registration should be a weak one or a normal one
@throws ServiceExistsException If a non-weak service has already been registered for TService
If the function returns successfully, from now on a service of the given type can be accessed
via the registry. The service is lazy initialized an will be created as soon
as it is requested for the first time. It is created by calling the given `fn` function.
`fn` must have the following signature:
@code{.cpp}
TService *fn(...);
@endcode
TService is the template parameter of this function. The arguments are variable, but if
arguments are present, they all must be pointers of types that are injectable via theregistry.
When the function is called internally, the registry will "inject" all services as parameters
of this function.
If the service is registered as weak, registering another service for the same TService will
not throw an exception but instead discard (and delete) this one.
@sa ServiceRegistry::registerInterface, ServiceRegistry::registerService,
ServiceRegistry::service
*/
/*!
@fn QtMvvm::ServiceRegistry::registerObject(TService *, bool)
@tparam TService The service to be registered by its own type. Must extend QObject
@param service The service instance to be registered as service
@param weak Specifies if the registration should be a weak one or a normal one
@throws ServiceExistsException If a non-weak service has already been registered for TService
If the function returns successfully, from now on a service of the given type can be accessed
via the registry. The service instance is from that point on owned by the registry. No DI is
performed on the passed service.
If the service is registered as weak, registering another service for the same TService will
not throw an exception but instead discard (and delete) this one.
@attention Make shure to register TInterface via QtMvvm::registerInterfaceConverter, otherwise
injection for the interface is not possible.
@sa ServiceRegistry::registerInterface, ServiceRegistry::registerService,
ServiceRegistry::service
*/
/*!
@fn QtMvvm::ServiceRegistry::registerService(const QByteArray &, const QMetaObject *, bool)
@param iid The interface id of the type to register the service for
@param metaObject The metaobject of the service to be registered for that interface. Must
extend QObject and implement the interface of `iid`, unless it is the id of the service itself
@param weak Specifies if the registration should be a weak one or a normal one
@throws ServiceExistsException If a non-weak service has already been registered for the iid
If the function returns successfully, from now on a service of the given metobject can be
accessed via the registry for the iid. The service is lazy initialized an will be created as
soon as it is requested for the first time. If it has any injectable properties, they will be
automatically injected on construction.
If the service is registered as weak, registering another service for the same iid will not
throw an exception but instead discard (and delete) this one.
@attention In order to be able to create the service, the class defined by the metaobject must
have an invokable constructor with the following signature:
`Q_INVOKABLE explicit Service(QObject *parent = nullptr);`
@sa ServiceRegistry::registerObject, QtMvvm::registerInterface, ServiceRegistry::serviceObj
*/
/*!
@fn QtMvvm::ServiceRegistry::registerService(const QByteArray &, const std::function<QObject*(const QObjectList &)> &, QByteArrayList, bool)
@param iid The interface id of the type to register the service for
@param fn The function to be called to construct the service
@param injectables The iids of the parameters to be passed to `fn`
@param weak Specifies if the registration should be a weak one or a normal one
@throws ServiceExistsException If a non-weak service has already been registered for the iid
If the function returns successfully, from now on a service of the given type can be accessed
via the registry for the interface. The service is lazy initialized an will be created as soon
as it is requested for the first time. It is created by calling the given `fn` function.
The function must return an object that implements the interface of `iid`, unless it is the id
of the service itself. The arguments are passed as object list and are determined by the
`injectables` parameter. That is a list of iids. When the function is called internally, the
registry will "inject" all services found by the iids as parameters of this function, comnined
to a list of objects.
If the service is registered as weak, registering another service for the same iid will not
throw an exception but instead discard (and delete) this one.
@sa ServiceRegistry::registerObject, QtMvvm::registerInterface, ServiceRegistry::serviceObj
*/
/*!
@fn QtMvvm::ServiceRegistry::service
@tparam TInterface The interface type get an instance of
@returns An object that implements the interface
@throws ServiceConstructionException If the registry failed to create an instance for that
interface
If the service is lazy inizialized and not created yet, the registry will do so. The returned
service is owned by the registry. Be careful with weak services, as they may be deleted at any
time. For normal services, this is never the case.
@note TInterface can either be an actual interface or a service type, depending on what you
registered it for (i.e. TInterface of the registerInterface() method and TService of the
registerObject() method
@sa ServiceRegistry::registerObject, QtMvvm::registerInterface, ServiceRegistry::serviceObj,
ServiceRegistry::injectServices, ServiceRegistry::constructInjected
*/
/*!
@fn QtMvvm::ServiceRegistry::serviceObj
@param iid The interface id of the type to get an instance of
@returns An object that implements the interface specified by iid
@throws ServiceConstructionException If the registry failed to create an instance for that
interface
If the service is lazy inizialized and not created yet, the registry will do so. The returned
service is owned by the registry. Be careful with weak services, as they may be deleted at any
time. For normal services, this is never the case.
@note iid must be the identity the service has been registered for. This depends on the type
itself and how it was registered.
@sa ServiceRegistry::registerService, ServiceRegistry::serviceObj,
ServiceRegistry::injectServices, ServiceRegistry::constructInjected
*/
/*!
@fn QtMvvm::ServiceRegistry::injectServices
@param object The object to inject properties into
@throws ServiceConstructionException If the registry failed to create an instance that needs
to be injected into the object
Loads all services that are needed to inject into properties of the object that are marked for
injection via #QTMVVM_INJECT. If any lazy service has not been created yet, it is created on
the fly and then injected.
@sa ServiceRegistry::constructInjected, ServiceRegistry::service, #QTMVVM_INJECT,
#QTMVVM_INJECT_PROP
*/
/*!
@fn QtMvvm::ServiceRegistry::constructInjected(QObject *)
@tparam TClass The type of object to be created, must extend QObject
@param parent The parent object of the created object
@returns A newly created instance of TClass.
@throws ServiceConstructionException If the registry failed to create an instance that needs
to be injected into the object
First the method creates a new instance of TClass, then loads all services that are needed to
inject into properties of the object that are marked for injection via #QTMVVM_INJECT. If any
lazy service has not been created yet, it is created on the fly and then injected. The
returned object is parented to the parent object. If that is null it is owned by the caller.
@attention For this to work, the TClass must have an invokable constructor with the following
signature: `Q_INVOKABLE explicit TClass(QObject *parent = nullptr);`
@sa ServiceRegistry::injectServices, ServiceRegistry::service, #QTMVVM_INJECT,
#QTMVVM_INJECT_PROP
*/
/*!
@fn QtMvvm::ServiceRegistry::constructInjected(const QMetaObject *, QObject *)
@param metaObject The metaobject of object type to be created, must extend QObject
@param parent The parent object of the created object
@returns A newly created instance of the given type.
@throws ServiceConstructionException If the registry failed to create an instance that needs
to be injected into the object
First the method creates a new instance of metaObject, then loads all services that are needed
to inject into properties of the object that are marked for injection via #QTMVVM_INJECT. If
any lazy service has not been created yet, it is created on the fly and then injected. The
returned object is parented to the parent object. If that is null it is owned by the caller.
@attention For this to work, the class defined by metaObject must have an invokable constructor
with the following signature: `Q_INVOKABLE explicit TClass(QObject *parent = nullptr);`
@sa ServiceRegistry::injectServices, ServiceRegistry::service, #QTMVVM_INJECT,
#QTMVVM_INJECT_PROP
*/

2
src/mvvmcore/coreapp.cpp

@ -162,7 +162,7 @@ void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariant
if(presenter) {
QPointer<ViewModel> vm;
try {
auto obj = ServiceRegistryPrivate::constructInjected(metaObject);
auto obj = ServiceRegistry::instance()->constructInjected(metaObject);
vm = qobject_cast<ViewModel*>(obj);
if(!vm)
throw ServiceConstructionException("Invalid types - not at QtMvvm::ViewModel");

33
src/mvvmcore/injection.h

@ -0,0 +1,33 @@
#ifndef QTMVVM_INJECTION_H
#define QTMVVM_INJECTION_H
#include "qtmvvmcore_global.h"
//! The primary namespace of the QtMvvm library
namespace QtMvvm {
//TODO sa in service registry
//! Registers QVariant converters from QObject to an interface type registered with Q_DECLARE_INTERFACE
template <typename TInterface>
inline void registerInterfaceConverter() {
QMetaType::registerConverter<QObject*, TInterface*>([](QObject *o) {
return qobject_cast<TInterface*>(o);
});
}
}
//! Mark a property for injection
#define QTMVVM_INJECT(classType, name) \
static inline QByteArray __qtmvvm_inject_##name() { \
return QtMvvm::__helpertypes::inject_iid<classType>(); \
} \
Q_PROPERTY(QByteArray __qtmvvm_inject_##name READ __qtmvvm_inject_##name STORED false SCRIPTABLE false DESIGNABLE false CONSTANT FINAL)
//! Create a for injection marked property based on a member
#define QTMVVM_INJECT_PROP(type, name, member) \
Q_PROPERTY(type name MEMBER member) \
QTMVVM_INJECT(type, name)
//! @file injection.h A header with injection related code
#endif // INJECTION_H

3
src/mvvmcore/mvvmcore.pro

@ -21,7 +21,8 @@ HEADERS += \
settingssetup.h \
settingssetuploader_p.h \
settingsviewmodel_p.h \
settingsviewmodel.h
settingsviewmodel.h \
injection.h
SOURCES += \
viewmodel.cpp \

23
src/mvvmcore/qtmvvmcore_global.h

@ -11,27 +11,4 @@
# define Q_MVVMCORE_EXPORT Q_DECL_IMPORT
#endif
//! The primary namespace of the QtMvvm library
namespace QtMvvm {
//! Registers QVariant converters from QObject to an interface type registered with Q_DECLARE_INTERFACE
template <typename TInterface>
inline void registerInterfaceConverter() {
QMetaType::registerConverter<QObject*, TInterface*>([](QObject *o) {
return qobject_cast<TInterface*>(o);
});
}
}
#define QTMVVM_INJECT(classType, name) \
static inline QByteArray __qtmvvm_inject_##name() { \
return QtMvvm::__helpertypes::inject_iid<classType>(); \
} \
Q_PROPERTY(QByteArray __qtmvvm_inject_##name READ __qtmvvm_inject_##name STORED false SCRIPTABLE false DESIGNABLE false CONSTANT FINAL)
#define QTMVVM_INJECT_PROP(type, name, member) \
Q_PROPERTY(type name MEMBER member) \
QTMVVM_INJECT(type, name)
#endif // QTMVVMCORE_GLOBAL_H

96
src/mvvmcore/serviceregistry.cpp

@ -56,6 +56,20 @@ QObject *ServiceRegistry::serviceObj(const QByteArray &iid)
throw ServiceDependencyException(iid);
}
void ServiceRegistry::injectServices(QObject *object)
{
auto &d = ServiceRegistry::instance()->d;
QMutexLocker _(&d->serviceMutex);
d->injectLocked(object);
}
QObject *ServiceRegistry::constructInjected(const QMetaObject *metaObject, QObject *parent)
{
auto &d = ServiceRegistry::instance()->d;
QMutexLocker _(&d->serviceMutex);
return d->constructInjectedLocked(metaObject, parent);
}
// ------------- Private Implementation -------------
bool ServiceRegistryPrivate::serviceBlocked(const QByteArray &iid) const
@ -67,55 +81,17 @@ bool ServiceRegistryPrivate::serviceBlocked(const QByteArray &iid) const
return false;
}
QObject *ServiceRegistryPrivate::constructInjected(const QMetaObject *metaObject)
{
auto &d = ServiceRegistry::instance()->d;
QMutexLocker _(&d->serviceMutex);
return d->constructInjectedLocked(metaObject);
}
QObject *ServiceRegistryPrivate::constructInjectedLocked(const QMetaObject *metaObject)
QObject *ServiceRegistryPrivate::constructInjectedLocked(const QMetaObject *metaObject, QObject *parent)
{
auto instance = metaObject->newInstance();
auto instance = metaObject->newInstance(Q_ARG(QObject*, parent));
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);
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 = services.value(iid);
if(!ref)
throw ServiceDependencyException(iid);
auto injObj = ref->instance(this, iid);
auto variant = QVariant::fromValue(injObj);
if(!variant.convert(tProp.userType())) {
throw ServiceConstructionException("Failed to convert QObject to interface with iid \"" +
iid +
"\". Use QtMvvm::registerInterfaceConverter to make it convertable "
"or change the property's type to \"QObject*\"");
}
tProp.write(instance, variant);
}
}
injectLocked(instance);
return instance;
} catch (...) {
instance->deleteLater();
@ -123,6 +99,42 @@ QObject *ServiceRegistryPrivate::constructInjectedLocked(const QMetaObject *meta
}
}
void ServiceRegistryPrivate::injectLocked(QObject *object)
{
static QRegularExpression nameRegex(QStringLiteral(R"__(^__qtmvvm_inject_(.+)$)__"),
QRegularExpression::OptimizeOnFirstUsageOption);
auto metaObject = object->metaObject();
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(object).toByteArray();
auto ref = services.value(iid);
if(!ref)
throw ServiceDependencyException(iid);
auto injObj = ref->instance(this, iid);
auto variant = QVariant::fromValue(injObj);
if(!variant.convert(tProp.userType())) {
throw ServiceConstructionException("Failed to convert QObject to interface with iid \"" +
iid +
"\". Use QtMvvm::registerInterfaceConverter to make it convertable "
"or change the property's type to \"QObject*\"");
}
tProp.write(object, variant);
}
}
}
ServiceRegistryPrivate::ServiceInfo::ServiceInfo(bool weak) :
@ -189,7 +201,7 @@ ServiceRegistryPrivate::MetaServiceInfo::MetaServiceInfo(const QMetaObject *meta
QObject *ServiceRegistryPrivate::MetaServiceInfo::construct(ServiceRegistryPrivate *d) const
{
auto instance = d->constructInjectedLocked(metaObject);
auto instance = d->constructInjectedLocked(metaObject, nullptr); //services are created without a parent
auto initMethod = metaObject->indexOfMethod("qtmvvm_init()"); //TODO document
if(initMethod != -1) {
auto method = metaObject->method(initMethod);

48
src/mvvmcore/serviceregistry.h

@ -8,93 +8,134 @@
#include <QtCore/qexception.h>
#include "QtMvvmCore/qtmvvmcore_global.h"
#include "QtMvvmCore/injection.h"
namespace QtMvvm {
class ServiceRegistryPrivate;
//! A singleton to prepare services for dependency injection and to access them
class Q_MVVMCORE_EXPORT ServiceRegistry
{
public:
//! @private
ServiceRegistry(ServiceRegistryPrivate *d_ptr);
~ServiceRegistry();
//! Returns the ServiceRegistry singleton instance
static ServiceRegistry* instance();
//! Checks if a given interface or service is already registered
template <typename TInterface>
bool isRegistered() const;
//! @copybrief ServiceRegistry::isRegistered()
bool isRegistered(const QByteArray &iid) const;
//! Register a service for its interface via the type
template <typename TInterface, typename TService>
void registerInterface(bool weak = false);
//! Register a service for its interface via a constructor function
template <typename TInterface, typename TService, typename TFunc>
void registerInterface(const TFunc &fn, bool weak = false);
//! Register a service for its interface via an already existing instance
template <typename TInterface, typename TService>
void registerInterface(TService *service, bool weak = false);
//! Register a service via its type
template <typename TService>
void registerObject(bool weak = false);
//! Register a service via a constructor function
template <typename TService, typename TFunc>
void registerObject(const TFunc &fn, bool weak = false);
//! Register a service via an already existing instance
template <typename TService>
void registerObject(TService *service, bool weak = false);
//! Register a service by an iid via their metadata
void registerService(const QByteArray &iid,
const QMetaObject *metaObject,
bool weak = false);
//! Register a service by an iid via a generalized constructor function
void registerService(const QByteArray &iid,
const std::function<QObject*(const QObjectList &)> &fn,
QByteArrayList injectables,
bool weak = false);
//! Returns the service for the given interface
template <typename TInterface>
TInterface *service();
//! Returns the service for the given iid
QObject *serviceObj(const QByteArray &iid);
//! Inject services for all injectable properties on object
void injectServices(QObject *object);
//! Constructs a new instance of TClass with properties injected
template <typename TClass>
TClass *constructInjected(QObject *parent = nullptr);
//! Constructs a new instance of metaObject with properties injected
QObject *constructInjected(const QMetaObject *metaObject, QObject *parent = nullptr);
private:
friend class QtMvvm::ServiceRegistryPrivate;
QScopedPointer<ServiceRegistryPrivate> d;
};
//! Is thrown if a service is beeing registered that is already registered
class Q_MVVMCORE_EXPORT ServiceExistsException : public QException
{
public:
//! @private
ServiceExistsException(const QByteArray &iid);
//! @inherit{QException::what}
const char *what() const noexcept override;
//! @inherit{QException::raise}
void raise() const override;
//! @inherit{QException::clone}
QException *clone() const override;
protected:
//! @private
ServiceExistsException(const ServiceExistsException * const other);
//! @private
const QByteArray _msg;
};
//! Is thrown in case the construction of a service has failed
class Q_MVVMCORE_EXPORT ServiceConstructionException : public QException
{
public:
//! @private
ServiceConstructionException(const QByteArray &message);
//! @inherit{QException::what}
const char *what() const noexcept override;
//! @inherit{QException::raise}
void raise() const override;
//! @inherit{QException::clone}
QException *clone() const override;
protected:
//! @private
ServiceConstructionException(const ServiceConstructionException * const other);
//! @private
const QByteArray _msg;
};
//! Is thrown if a service could not be created because of a missing dependency to be injected
class Q_MVVMCORE_EXPORT ServiceDependencyException : public ServiceConstructionException
{
public:
//! @private
ServiceDependencyException(const QByteArray &iid);
void raise() const override;
QException *clone() const override;
protected:
//! @private
ServiceDependencyException(const ServiceDependencyException * const other);
};
@ -174,6 +215,13 @@ TInterface *ServiceRegistry::service()
return qobject_cast<TInterface*>(serviceObj(__helpertypes::inject_iid<TInterface*>()));
}
template<typename TClass>
TClass *ServiceRegistry::constructInjected(QObject *parent)
{
static_assert(__helpertypes::is_qobj<TClass>::value, "TClass must be a qobject class");
return qobject_cast<TClass*>(constructInjected(&TClass::staticMetaObject, parent));
}
}
#endif // QTMVVM_SERVICEREGISTRY_H

4
src/mvvmcore/serviceregistry_p.h

@ -55,8 +55,8 @@ public:
QHash<QByteArray, QSharedPointer<ServiceInfo>> services;
bool serviceBlocked(const QByteArray &iid) const;
static QObject *constructInjected(const QMetaObject *metaObject);
QObject *constructInjectedLocked(const QMetaObject *metaObject);
QObject *constructInjectedLocked(const QMetaObject *metaObject, QObject *parent);
void injectLocked(QObject *object);
};
}

1
src/mvvmcore/settingssetup.h

@ -12,6 +12,7 @@
namespace QtMvvm {
//! A sub namespace for the different elements in a settings setup
namespace SettingsElements {
struct Entry

1
src/mvvmcore/viewmodel.h

@ -9,6 +9,7 @@
#include <QtCore/qpointer.h>
#include "QtMvvmCore/qtmvvmcore_global.h"
#include "QtMvvmCore/injection.h"
namespace QtMvvm {

3
sync.profile

@ -12,5 +12,6 @@ $publicclassregexp = "QtMvvm::(?!__helpertypes|SettingsElements).+";
%classnames = (
"message.h" => "Messages",
"settingssetup.h" => "SettingsElements"
"settingssetup.h" => "SettingsElements",
"injection.h" => "Injection",
);

Loading…
Cancel
Save