13 changed files with 628 additions and 72 deletions
@ -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 |
||||
|
*/ |
@ -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 |
||||
|
*/ |
@ -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
|
Loading…
Reference in new issue