1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Internal.Unique;
25 
26 debug import std.stdio;
27 private import Dgame.Internal.Allocator;
28 private import cstd = core.stdc.stdlib;
29 
30 @property
31 string idOf(T)() {
32 	static if (__traits(compiles, { string s = T.stringof; }))
33 		return T.stringof;
34 	else
35 		return __traits(identifier, T);
36 }
37 
38 void _def_del(T = void)(T* p) {
39 	cstd.free(p);
40 }
41 
42 struct unique_ptr(T) {
43 	T* ptr;
44 	void function(T*) del_func;
45 	
46 	this(T* p, void function(T*) df = &_def_del!(T)) {
47 		this.ptr = p;
48 		this.del_func = df;
49 	}
50 	
51 	this(ref T* p, void function(T*) df = &_def_del!(T)) {
52 		this.ptr = p;
53 		this.del_func = df;
54 		
55 		p = null;
56 	}
57 	
58 	@disable
59 	this(this);
60 	
61 	~this() {
62 		if (this.del_func is null || this.ptr is null) {
63 			return;
64 		}
65 
66 		debug writeln("Terminate unique ", idOf!(T));
67 
68 		this.del_func(this.ptr);
69 	}
70 	
71 	T* release() pure nothrow {
72 		scope(exit) this.ptr = null;
73 		return this.ptr;
74 	}
75 
76 	bool isValid() const pure nothrow {
77 		return this.ptr !is null;
78 	}
79 
80 	alias ptr this;
81 }
82 
83 unique_ptr!(T) move(T)(ref unique_ptr!(T) uniq) {
84 	return unique_ptr!(T)(uniq.release(), uniq.del_func);
85 }
86 
87 unique_ptr!(T) make_unique(T)(auto ref T value, void function(T*) df = &_def_del!(T)) if (!is(T : U*, U)) {
88 	T* p;
89 	make(value, p);
90 	
91 	return make_unique(p, df);
92 }
93 
94 unique_ptr!(T) make_unique(T, Args...)(void function(T*) df, Args args) {
95 	import std.conv : emplace;
96 	
97 	T* p = alloc_new!(T)(1);
98 	emplace(p, args);
99 	
100 	return make_unique(p, df);
101 }
102 
103 unique_ptr!(T) make_unique(T, Args...)(Args args) {
104 	import std.conv : emplace;
105 	
106 	T* p = alloc_new!(T)(1);
107 	emplace(p, args);
108 	
109 	return make_unique(p);
110 }
111 
112 unique_ptr!(T) make_unique(T)(auto ref T* p, void function(T*) df = &_def_del!(T)) {
113 	return unique_ptr!(T)(p, df);
114 }
115 
116 unique_ptr!(T) allocate_unique(T)(size_t count, void function(T*) df = &_def_del!(T)) {
117 	T* p = alloc_new!(T)(count);
118 	
119 	return make_unique(p, df);
120 }
121 
122 unittest {
123 	struct A {
124 		int id;
125 	}
126 	
127 	unique_ptr!(A) test(unique_ptr!(A) rhs) {
128 		assert(rhs.isValid());
129 		assert(rhs.id == 42);
130 		
131 		return move(rhs);
132 	}
133 	
134 	unique_ptr!(A) as = new A(42);
135 	
136 	assert(as.isValid());
137 	assert(as.id == 42);
138 	
139 	unique_ptr!(A) as2 = move(as);
140 	
141 	assert(!as.isValid());
142 	assert(as2.isValid());
143 	assert(as2.id == 42);
144 	
145 	unique_ptr!(A) as3 = test(move(as2));
146 	
147 	assert(!as2.isValid());
148 	assert(as3.isValid());
149 	assert(as3.id == 42);
150 }