1 #!/usr/bin/env dub 2 /+ dub.sdl: 3 name "example" 4 dependency "automem" version="~>0.3.3" 5 dependency "test_allocator" version="~>0.2.1" 6 +/ 7 module nxt.app; 8 9 import std.experimental.allocator; 10 import std.stdio; 11 import std.typecons; 12 13 import nxt.automem; 14 import nxt.test_allocator; 15 16 alias Tup = Tuple!(int, int); 17 18 struct A(Allocator) 19 { 20 UniqueArray!(int, Allocator) data; 21 alias data this; 22 23 this(Allocator alloc) 24 { 25 data = typeof(data)(alloc); 26 } 27 28 // This is how I would like to implement the move (kind of). 29 // Is there a better way? 30 this(typeof(data) other) 31 { 32 this.data = other.move; 33 } 34 35 auto move() 36 { 37 return typeof(this)(data.move); 38 } 39 } 40 41 // My problem is that I had to use this construction to make AA work. 42 // This in turn lead to e.g. length segfaulting after "move". 43 // Which lead to it being very, very easy to introduce bugs in the software. 44 struct Aw(Allocator) 45 { 46 alias ArrT = UniqueArray!(Tup, Allocator); 47 RefCounted!(ArrT, Allocator) data; 48 49 ref auto get() return inout 50 { 51 return *data; 52 } 53 54 alias get this; 55 56 this(Allocator alloc) 57 { 58 data = typeof(data)(alloc, alloc); 59 } 60 61 this(RefCounted!(ArrT, Allocator) d) 62 { 63 data = d; 64 } 65 66 // How to implement a "move" that is easy to use correctly? 67 // This is not correct... wrong type. 68 // Unable to instantiate Aw without either `move` taking an allocator 69 // parameter or storing an allocator in Aw. 70 ArrT move() 71 { 72 return (*data).move; 73 } 74 75 // this lead to segfaults.... 76 auto move2() 77 { 78 auto tmp = data; 79 // probably not a good idea.. wrong usage. I want to release the 80 // RefCounted so this instance and the returned do not refer to the 81 // same underlying data. 82 destroy(data); 83 return typeof(this)(tmp); 84 } 85 } 86 87 struct AA(T, Allocator) 88 { 89 UniqueArray!(T!Allocator, Allocator) data; 90 alias data this; 91 92 this(Allocator alloc) 93 { 94 data = typeof(data)(alloc); 95 } 96 } 97 98 void main(string[] args) 99 { 100 TestAllocator talloc; 101 alias Alloc = typeof(&talloc); 102 103 auto a = A!Alloc(&talloc); 104 105 // this do not work 106 //auto aa = AA!(A!Alloc, Alloc)(&talloc); 107 108 // but this do 109 auto aa = AA!(Aw!Alloc, Alloc)(&talloc); 110 111 // some examples of how to use 112 writeln("aa len: ", aa.length); // 0 113 aa ~= Aw!Alloc(&talloc); 114 aa ~= Aw!Alloc(&talloc); 115 writeln("aa len: ", aa.length); // 2 116 aa[0] ~= Tup(1, 2); 117 aa[1] ~= Tup(3, 2); 118 writeln(aa[0][]); // [2] 119 120 // lets "move" an object 121 auto moved_a = aa[1].move; // but moved_a isn't an Aw type anymore :/ 122 writeln("aa len: ", aa.length); // 2 123 writeln("moved_a len: ", moved_a.length); 124 125 // shrink to remove the moved element 126 aa.length = 1; 127 writeln("aa[1] len: ", aa[1].length); 128 writeln("moved_a len: ", moved_a.length); 129 130 // lets see what happens with move2 131 auto move2_a = aa[0].move2; // nice the type is Aw! as expected 132 writeln("move2_a len: ", move2_a.length); 133 // this will segfault. this is why it is so error prone to do it this way. 134 writeln("aa[0] len: ", aa[0].length); 135 }