atilaneves / automem
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 .

Loading