Skip to content

Commit b2b183b

Browse files
committed
README with some comments in the code added
1 parent 8005d66 commit b2b183b

File tree

6 files changed

+158
-7
lines changed

6 files changed

+158
-7
lines changed

README.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# About
2+
3+
*DIContainerCpp* is a simple but powerful dependency-injection container for C++.
4+
5+
# Usage
6+
7+
DIContainerCpp is inspired by Microsoft's COM object model.
8+
Every interface managed by with library must implement `IUnknown`, and have a unique ID in additon to that.
9+
10+
## Writing components
11+
12+
```c++
13+
using UID = long;
14+
15+
const UID IUnknown = 1;
16+
17+
struct IUnknown
18+
{
19+
virtual unsigned int AddRef() = 0; // Increase the interface's RefCount
20+
virtual unsigned int Release() = 0; // Decrease the interface's RefCount. If zero, the component must be free itself.
21+
virtual IUnknown* QueryInterface(UID id) = 0; // Return the implementation of the interface known by `id`.
22+
};
23+
```
24+
25+
Every component is a collection of one-or-more interfaces and must implement `IComponent` (which in-turn derives from `IUnknown`):
26+
27+
```c++
28+
struct IComponent : IUnknown
29+
{
30+
// Return a list of interfaces that this component requires
31+
virtual std::vector<UID>& GetDependencies() = 0;
32+
// Inject the interface `dependency` with `id` into this component.
33+
// `id` will have been returned by `GetDependencies()`
34+
virtual void SetDependency(UID id, IUnknown* dependency) = 0;
35+
};
36+
```
37+
38+
## Using the container
39+
40+
The container itself, `DIContainer`, implements `IContainer`:
41+
42+
```c++
43+
struct IContainer
44+
{
45+
// Return the registered interface by `id`
46+
virtual IUnknown* GetInterface(UID id) = 0;
47+
};
48+
49+
enum class CreationPolicy
50+
{
51+
NewInstance, // New instance for every GetInterface() call
52+
SharedInstance // One instance per container
53+
};
54+
55+
// E.g. []()->IComponent* { return new MyFancyCustomComponent(); }
56+
typedef IComponent* (*CreateInstanceFunc)(void);
57+
58+
class DIContainer : IContainer {
59+
public:
60+
// Register a component with the unique `id` to be lazily created using a `createFn` and a creation `policy`
61+
void RegisterInterface(UID id, CreateInstanceFunc createFn, CreationPolicy policy=CreationPolicy::NewInstance);
62+
virtual IUnknown* GetInterface(UID id) override;
63+
}
64+
```
65+
66+
Note that cyclic dependencies are detected automatically and raise a `DIContainerException`.
67+
68+
Example of using a DIContainer.
69+
70+
```c++
71+
// Container initialization
72+
DIContainer cont;
73+
// Register components in the container
74+
try {
75+
cont.RegisterInterface(UIDComponent3, []()->IComponent* { return new Component3; });
76+
cont.RegisterInterface(UIDComponent4, []()->IComponent* { return new Component4; });
77+
}
78+
catch (const DIContainerExeption& ex) {
79+
}
80+
81+
// using components
82+
ComPtr<IComponent1, UIDComponent1> cmp1(cont.GetInterface(UIDComponent1));
83+
int res1 = cmp1->Calculate(val);
84+
85+
ComPtr<IComponent2, UIDComponent2> cmp2 = (IComponent2*)cont.GetInterface(UIDComponent2);
86+
int res2 = cmp2->Calculate(val);
87+
```
88+
89+
See [tests/](tests/) for more examples.
90+
91+
# Hacking
92+
93+
## Building
94+
95+
This project uses the CMake build-system. To build it, simply run the following commands:
96+
97+
```console
98+
$ mkdir build
99+
$ cd build/
100+
$ cmake ..
101+
$ make
102+
```
103+
104+
## Running tests
105+
106+
This project uses *GoogleTest*.
107+
108+
1. Build the project
109+
2. `cd build/ && ./runUnitTests`

src/DIContainer.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ enum class CreationPolicy
1212
SharedInstance
1313
};
1414

15+
// E.g. []()->IComponent* { return new MyFancyCustomComponent(); }
1516
typedef IComponent* (*CreateInstanceFunc)(void);
1617

1718
class DIContainerExeption : public std::runtime_error
@@ -27,9 +28,11 @@ class DIContainer : public IContainer
2728
public:
2829
DIContainer();
2930
~DIContainer();
30-
31-
// IContainer implementation
31+
32+
// Register a component with the unique `id` to be lazily created using a `createFn` and a creation `policy`
3233
void RegisterInterface(UID id, CreateInstanceFunc createFn, CreationPolicy policy=CreationPolicy::NewInstance);
34+
35+
// IContainer implementation
3336
virtual IUnknown* GetInterface(UID id) override;
3437

3538
private:

src/IComponent.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const UID UIDIComponent = 1;
66

77
struct IComponent : IUnknown
88
{
9+
// Return a list of interfaces that this component requires
910
virtual std::vector<UID>& GetDependencies() = 0;
11+
// Inject the interface `dependency` with `id` into this component.
12+
// `id` will have been returned by `GetDependencies()`
1013
virtual void SetDependency(UID id, IUnknown* dependency) = 0;
1114
};

src/IContainer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33

44
struct IContainer
55
{
6+
// Return the registered in the DIContainer interface by `id`
67
virtual IUnknown* GetInterface(UID id) = 0;
78
};

src/IUnknown.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ using UID = long;
55

66
const UID UIDIUnknown = 1;
77

8+
// `IUnknown` is the base for all interfaces managed with this library
89
struct IUnknown
910
{
10-
virtual unsigned int AddRef() = 0;
11-
virtual unsigned int Release() = 0;
12-
virtual IUnknown* QueryInterface(UID id) = 0;
13-
};
11+
virtual unsigned int AddRef() = 0; // Increase the interface's RefCount
12+
virtual unsigned int Release() = 0; // Decrease the interface's RefCount. If zero, the component must be free itself.
13+
virtual IUnknown* QueryInterface(UID id) = 0; // Return the implementation of the interface known by `id`.
14+
};

tests/DIContainerTests.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <string>
44

55
#include "../src/DIContainer.h"
6+
#include "../src/ComPtr.h"
7+
68
#include "component1.h"
79
#include "component2.h"
810
#include "component3.h"
@@ -12,7 +14,39 @@ IComponent* CreateCmp1() {
1214
return new Component1;
1315
}
1416

15-
TEST(DIContainer, TEST1) {
17+
TEST(DIContainer, ComPtr) {
18+
DIContainer cont;
19+
// Register components in the container
20+
cont.RegisterInterface(UIDComponent1, CreateCmp1, CreationPolicy::SharedInstance);
21+
cont.RegisterInterface(UIDComponent2, []()->IComponent* { return new Component2; });
22+
23+
int val = 12;
24+
ComPtr<IComponent1, UIDComponent1> cmp1(cont.GetInterface(UIDComponent1));
25+
int res1 = cmp1->Calculate(val);
26+
EXPECT_EQ(res1, val * val);
27+
28+
ComPtr<IComponent2, UIDComponent2> cmp2 = (IComponent2*)cont.GetInterface(UIDComponent2);
29+
int res2 = cmp2->Calculate(val);
30+
EXPECT_EQ(res2, val * val * 2);
31+
}
32+
33+
TEST(DIContainer, ComPtrUsage) {
34+
DIContainer cont;
35+
cont.RegisterInterface(UIDComponent1, CreateCmp1, CreationPolicy::SharedInstance);
36+
cont.RegisterInterface(UIDComponent2, []()->IComponent* { return new Component2; });
37+
38+
int val = 12;
39+
IComponent1* cmp1 = (IComponent1*)cont.GetInterface(UIDComponent1);
40+
int res1 = cmp1->Calculate(val);
41+
EXPECT_EQ(res1, val * val);
42+
43+
IComponent2* cmp2 = (IComponent2*)cont.GetInterface(UIDComponent2);
44+
int res2 = cmp2->Calculate(val);
45+
EXPECT_EQ(res2, val * val * 2);
46+
}
47+
48+
49+
TEST(DIContainer, AddRefRelease) {
1650
DIContainer cont;
1751
cont.RegisterInterface(UIDComponent1, CreateCmp1, CreationPolicy::SharedInstance);
1852
cont.RegisterInterface(UIDComponent2, []()->IComponent* { return new Component2; });

0 commit comments

Comments
 (0)