1 /** 2 C++-style automatic memory management smart pointers for D using `stdx.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. The examples 10 above will be explicit. 11 12 Another reason to have to pass in the type of allocator is to decide how it is to 13 be stored. Stateless allocators can be "stored" by value and imply zero-cost `Unique` pointers. 14 Singleton allocators such as Mallocator (that have an `instance` attribute/member function) 15 don't need to be passed in to the constructor. This is detected at compile-time as an example 16 of design by instrospection. 17 18 `RefCounted` leverages D's type system by doing atomic reference counting *iff* the type of the contained 19 object is `shared`. Otherwise it's non-atomic. 20 */ 21 module automem; 22 23 public import automem.unique; 24 public import automem.unique_array; 25 public import automem.ref_counted; 26 27 import automem.test_utils: TestUtils; 28 29 mixin TestUtils; 30 31 /** 32 This unittest can be @safe if the allocator has @safe functions 33 */ 34 @system @nogc unittest { 35 36 import stdx.allocator.mallocator: Mallocator; 37 import std.algorithm: move; 38 39 struct Point { 40 int x; 41 int y; 42 } 43 44 { 45 // must pass arguments to initialise the contained object 46 auto u1 = Unique!(Point, Mallocator)(2, 3); 47 assert(*u1 == Point(2, 3)); 48 assert(u1.y == 3); 49 50 // auto u2 = u1; // won't compile, can only move 51 typeof(u1) u2 = u1.move; 52 assert(cast(bool)u1 == false); // u1 is now empty 53 } 54 // memory freed for the Point structure created in the block 55 56 { 57 auto s1 = RefCounted!(Point, Mallocator)(4, 5); 58 assert(*s1 == Point(4, 5)); 59 assert(s1.x == 4); 60 { 61 auto s2 = s1; // can be copied 62 } // ref count goes to 1 here 63 64 } // ref count goes to 0 here, memory released 65 66 { 67 // the constructor can also take (size, init) or (size, range) values 68 auto arr = UniqueArray!(Point, Mallocator)(3); 69 70 const Point[3] expected1 = [Point(), Point(), Point()]; // because array literals aren't @nogc 71 assert(arr[] == expected1); 72 73 const Point[1] expected2 = [Point()]; 74 arr.length = 1; 75 assert(*arr == expected2); //deferencing is the same as slicing all of it 76 77 arr ~= UniqueArray!(Point, Mallocator)(1, Point(6, 7)); 78 const Point[2] expected3 = [Point(), Point(6, 7)]; 79 assert(arr[] == expected3); 80 81 } // memory for the array released here 82 } 83 84 /// 85 @("theAllocator") 86 @system unittest { 87 with(theTestAllocator) { 88 auto ptr = Unique!int(42); 89 assert(*ptr == 42); 90 } 91 // TestAllocator will throw here if any memory leaks 92 }