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 }