Update
1 |
/**
|
|
2 |
C++-style automatic memory management smart pointers for D using `std.experimental.allocator`.
|
|
3 |
|
|
4 |
Unlike the C++ variants, the smart pointers themselves allocate the memory for the objects they contain.
|
|
5 |
That ensures the right allocator is used to dispose of the memory as well.
|
|
6 |
|
|
7 |
Allocators are template arguments instead of using `theAllocator` so
|
|
8 |
that these smart pointers can be used in `@nogc` code. However, they
|
|
9 |
will default to `typeof(theAllocator)` for simplicity.
|
|
10 |
|
|
11 |
Another reason to have to pass in the type of allocator is to decide how it is to
|
|
12 |
be stored. Stateless allocators can be "stored" by value and imply zero-cost `Unique` pointers.
|
|
13 |
Singleton allocators such as Mallocator (that have an `instance` attribute/member function)
|
|
14 |
don't need to be passed in to the constructor. This is detected at compile-time as an example
|
|
15 |
of design by instrospection.
|
|
16 |
|
|
17 |
`RefCounted` leverages D's type system by doing atomic reference counting *iff* the type of the contained
|
|
18 |
object is `shared`. Otherwise it's non-atomic.
|
|
19 |
*/
|
|
20 |
module automem; |
|
21 |
|
|
22 |
public import automem.unique; |
|
23 |
public import automem.ref_counted; |
|
24 |
public import automem.vector; |
|
25 |
public import automem.array; |
|
26 |
|
|
27 |
|
|
28 |
@safe unittest { |
|
29 |
|
|
30 |
import std.algorithm: move; |
|
31 |
|
|
32 |
static struct Point { |
|
33 |
int x; |
|
34 |
int y; |
|
35 |
}
|
|
36 |
|
|
37 |
// set theAllocator as desired beforehand, e.g.
|
|
38 |
// theAllocator = allocatorObject(Mallocator.instance)
|
|
39 |
|
|
40 |
{
|
|
41 |
// must pass arguments to initialise the contained object
|
|
42 | 2 |
auto u1 = Unique!Point(2, 3); |
43 | 2 |
assert(*u1 == Point(2, 3)); |
44 | 2 |
assert(u1.y == 3); |
45 |
|
|
46 |
// auto u2 = u1; // won't compile, can only move
|
|
47 | 2 |
typeof(u1) u2 = () @trusted { return u1.move; }(); |
48 | 2 |
assert(cast(bool)u1 == false); // u1 is now empty |
49 |
}
|
|
50 |
// memory freed for the Point structure created in the block
|
|
51 |
|
|
52 |
{
|
|
53 | 2 |
auto s1 = RefCounted!Point(4, 5); |
54 | 2 |
assert(*s1 == Point(4, 5)); |
55 | 2 |
assert(s1.x == 4); |
56 |
{
|
|
57 | 2 |
auto s2 = s1; // can be copied |
58 |
} // ref count goes to 1 here |
|
59 |
|
|
60 |
} // ref count goes to 0 here, memory released |
|
61 |
|
|
62 |
{
|
|
63 |
import std.algorithm: map, equal; |
|
64 |
import std.range: iota; |
|
65 |
|
|
66 |
// `vector` is also known as `array`
|
|
67 | 2 |
auto vec = vector(Point(1, 2), Point(3, 4), Point(5, 6)); |
68 | 2 |
assert(equal(vec.range, [Point(1, 2), Point(3, 4), Point(5, 6)])); |
69 |
|
|
70 |
// reallocations are @system since old pointers can dangle
|
|
71 | 2 |
() @trusted { |
72 | 2 |
vec.length = 1; |
73 | 2 |
assert(equal(vec.range, [Point(1, 2)])); |
74 |
|
|
75 | 2 |
vec ~= Point(7, 8); |
76 | 2 |
assert(equal(vec.range, [Point(1, 2), Point(7, 8)])); |
77 |
|
|
78 | 2 |
vec ~= 2.iota.map!(i => Point(i + 10, i + 11)); |
79 | 2 |
assert(equal(vec.range, [Point(1, 2), Point(7, 8), Point(10, 11), Point(11, 12)])); |
80 |
}();
|
|
81 |
} // memory for the array released here |
|
82 |
}
|
|
83 |
|
|
84 |
|
|
85 |
// @nogc test - must explicitly use the allocator for compile-time guarantees
|
|
86 |
@safe @nogc unittest { |
|
87 |
import std.experimental.allocator.mallocator: Mallocator; |
|
88 |
|
|
89 |
static struct Point { |
|
90 |
int x; |
|
91 |
int y; |
|
92 |
}
|
|
93 |
|
|
94 |
{
|
|
95 |
// must pass arguments to initialise the contained object
|
|
96 | 2 |
auto u1 = Unique!(Point, Mallocator)(2, 3); |
97 | 2 |
assert(*u1 == Point(2, 3)); |
98 | 2 |
assert(u1.y == 3); |
99 |
}
|
|
100 |
// memory freed for the Point structure created in the block
|
|
101 |
|
|
102 |
// similarly for the other types
|
|
103 |
}
|
Read our documentation on viewing source code .