File: README.md

package info (click to toggle)
injeqt 1.1.0-1.1
  • links: PTS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 980 kB
  • ctags: 1,581
  • sloc: cpp: 8,122; ansic: 19; sh: 15; makefile: 4
file content (245 lines) | stat: -rw-r--r-- 6,938 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
injeqt
======

Dependency injection framework for Qt

Description
-----------

Dependency injection is an implementation of inverse of control and can be used
to increase program modularity and extendability. In general it also results in more
testable source code.

There is a lot of dependency injection frameworks for bytecode based languages like
Java and C#, for instance Guice and Spring. C++ lacks reflection facilities that make
dependency injection so powerfull in these languages.

Injeqt is an attempt to build a powerful and reliable dependency injection framework
on Qt's reflection based on MOC (meta-object compiler). For version 0.1 some basic
features were implemented.

Features
--------

*QObject based services*

All services dependencies in injeqt must be QObject descendents. This is requirement
of Qt meta object system.

*Setter-based injection of QObject services*

All dependencies are injected by setters. Setters are defined in `private slots` part
of class and must be marked with `INJEQT_SET` tag (`INJEQT_SETTER` is also available for
backwards compatibility).

Additional slots can be marked with `INJEQT_INIT` and `INJEQT_DONE` tags. `INJEQT_INIT`
methods are called after all injeqt setters are called - this allows for initializing of
object when all services are available. `INJEQT_DONE` methods are called before injector
with all its object is destroyed. This allows for gracefull shutdown with all services
still available (destruction will follow calling all `INJEQT_DONE` methods in system).

*Several methods of object creation*

Injeqt can create object using default contructor or factories. It can also receive ready
objects from outside (called ready-objects). Resolving of dependencies and calling injeqt
setters, init and done methods is only done for objects created by injeqt itself - with
default constructor method (added with add_type<T>()). It can change in future versions
of injeqt.

*Injection into objects*

Injeqt can also call `INJEQT_SET` methods on objects created outside injector infrastructure.
Call injector.injeqt_into(QObject *) to do that. It will also call `INJEQT_INIT` methods
after setting all services. No `INJEQT_DONE` methods are called.

*Small interface*

Injeqt exposes only very limited interface to allow internals to change freely while maintaining
ABI and API.

Qt relationshipt
----------------

Injeqt requires Qt for its reflection capabilities.

*QObject*

Injeqt only recognizes QObject-derived types. That may change when reflection get accepted into one
of the next C++ standards.

Plans
-----

*1.1*

* add on-demand and immediate object creation modes

*1.2*

* more initialization checking (searching factory loops)

*1.3*

* (maybe) contructor injection
* (maybe) more dynamic type and dependencies resolutions

Example
-------

Here is example of what can be done using injeqt:

	#include <injeqt/injector.h>
	#include <injeqt/module.h>

	#include <QtCore/QObject>
	#include <iostream>
	#include <memory>
	#include <string>

	class hello_service : public QObject
	{
		Q_OBJECT

	public:
		hello_service() {}
		virtual ~hello_service() {}

		std::string say_hello() const
		{
			return {"Hello"};
		}
	};

	class world_service : public QObject
	{
		Q_OBJECT

	public:
		world_service() {}
		virtual ~world_service() {}

		std::string say_world() const
		{
			return {"World"};
		}
	};

	class hello_factory : public QObject
	{
		Q_OBJECT

	public:
		Q_INVOKABLE hello_factory() {}
		virtual ~hello_factory() {}

		Q_INVOKABLE hello_service * create_service()
		{
			return new hello_service{};
		}
	};

	class hello_client : public QObject
	{
		Q_OBJECT

	public:
		Q_INVOKABLE hello_client() : _s{nullptr}, _w{nullptr} {}
		virtual ~hello_client() {}

		std::string say() const
		{
			return _s->say_hello() + " " + _w->say_world() + "!";
		}

	private slots:
		INJEQT_INIT void init()
		{
			std::cerr << "all services set" << std::endl;
		}

		INJEQT_DONE void done()
		{
			std::cerr << "ready for destruction" << std::endl;
		}

		INJEQT_SET void set_hello_service(hello_service *s)
		{
			_s = s;
		}

		INJEQT_SET void set_world_service(world_service *w)
		{
			_w = w;
		}

	private:
		hello_service *_s;
		world_service *_w;

	};

	class module : public injeqt::module
	{
	public:
		explicit module()
		{
			_w = std::unique_ptr<world_service>{new world_service{}};

			add_type<hello_client>();
			add_type<hello_factory>();
			add_factory<hello_service, hello_factory>();
			add_ready_object<world_service>(_w.get());
		}

		virtual ~module() {}

	private:
		std::unique_ptr<world_service> _w;

	};

	int main()
	{
		auto modules = std::vector<std::unique_ptr<injeqt::module>>{};
		modules.emplace_back(std::unique_ptr<injeqt::module>{new module{}});

		auto injector = injeqt::injector{std::move(modules)};
		auto client = injector.get<hello_client>();
		auto hello = client->say();

		std::cout << hello << std::endl;
	}

	#include "hello-world.moc"

In that example we can see two main services names `hello_service` and `world_service`. There
is also client of these names `hello_client`. In `module` class we configure how we create
and access these instances.

`hello_client` is added using `add_type` function. It means that injeqt will try to create it
using default constructor. We provide that by declaration of `Q_INVOKABLE hello_client()`
(`Q_INVOKABLE` is requires by Qt's meta object system).

`hello_service` is added using `add_factory` function. It means that injeqt will first try to
create a `hello_factory` object, then it will look for a method of that objet that returns
`hello_service` object. It will find `Q_INVOKABLE hello_service * create_service()` and use it.

To be able to create `hello_factory` injeqt must know about it, so we also add it using `add_type`
method.

Last, `world_service`, is added as a ready object - provided from outside of injeqt scope.

In `main` method list of conifguration modules are passed to newly created `injector` instance.
From that moment, we can use `injector` to create and manage our services. Just one line below
an `hello_client` instance is required. This is what happens next:

* injeqt looks for dependencies of `hello_client` and found that it first needs to create `hello_factory`
* injeqt creates `hello_factory` without problems, as it does not have dependencies of its own
* injeqt adds new instance to object pool
* injeqt calls `hello_factory::create_service()` methods and ads its result to object pool
* now all dependencies of `hello_client` are available, so new instance of it is created with
  default constructor and its added to objec tpool
* all methods of `hello_client` marked with `INJEQT_SET` are called with proper objects from pool
* all methods of `hello_client` marked with `INJEQT_INIT` are called
* this instance is returned to caller
* before injector is destructed, all methods of `hello_client` marked with `INJEQT_DONE` are called