1 /**
2  * Metaprogramming miscellanea
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.meta.misc;
15 
16 import std.algorithm;
17 import std.traits;
18 
19 /**
20  * Same as TypeTuple, but meant to be used with values.
21  *
22  * Example:
23  *   foreach (char channel; ValueTuple!('r', 'g', 'b'))
24  *   {
25  *     // the loop is unrolled at compile-time
26  *     // "channel" is a compile-time value, and can be used in string mixins
27  *   }
28  */
29 template ValueTuple(T...)
30 {
31 	alias T ValueTuple;
32 }
33 
34 template RangeTupleImpl(size_t N, R...)
35 {
36 	static if (N==R.length)
37 		alias R RangeTupleImpl;
38 	else
39 		alias RangeTupleImpl!(N, ValueTuple!(R, R.length)) RangeTupleImpl;
40 }
41 
42 /// Generate a tuple containing integers from 0 to N-1.
43 /// Useful for static loop unrolling. (staticIota)
44 template RangeTuple(size_t N)
45 {
46 	alias RangeTupleImpl!(N, ValueTuple!()) RangeTuple;
47 }
48 
49 template ArrayToTuple(alias arr, Elements...)
50 {
51 	static if (arr.length)
52 		alias ArrayToTuple = ArrayToTuple!(arr[1..$], ValueTuple!(Elements, arr[0]));
53 	else
54 		alias ArrayToTuple = Elements;
55 }
56 
57 unittest
58 {
59 	alias X = ArrayToTuple!"abc";
60 	static assert(X[0] == 'a' && X[2] == 'c');
61 	static assert([X] == "abc");
62 }
63 
64 /// Return something to foreach over optimally.
65 /// If A is known at compile-time, return a tuple,
66 /// so the foreach is unrolled at compile-time.
67 /// Otherwise, return A for a regular runtime foreach.
68 template CTIterate(alias A)
69 {
70 	static if (is(typeof(ArrayToTuple!A)))
71 		enum CTIterate = ArrayToTuple!A;
72 	else
73 		alias CTIterate = A;
74 }
75 
76 unittest
77 {
78 	foreach (c; CTIterate!"abc") {}
79 	string s;
80 	foreach (c; CTIterate!s) {}
81 }
82 
83 /// Like std.typecons.Tuple, but a template mixin.
84 /// Unlike std.typecons.Tuple, names may not be omitted - but repeating types may be.
85 /// Example: FieldList!(ubyte, "r", "g", "b", ushort, "a");
86 mixin template FieldList(Fields...)
87 {
88 	mixin(GenFieldList!(void, Fields));
89 }
90 
91 template GenFieldList(T, Fields...)
92 {
93 	static if (Fields.length == 0)
94 		enum GenFieldList = "";
95 	else
96 	{
97 		static if (is(typeof(Fields[0]) == string))
98 			enum GenFieldList = T.stringof ~ " " ~ Fields[0] ~ ";\n" ~ GenFieldList!(T, Fields[1..$]);
99 		else
100 			enum GenFieldList = GenFieldList!(Fields[0], Fields[1..$]);
101 	}
102 }
103 
104 unittest
105 {
106 	struct S
107 	{
108 		mixin FieldList!(ubyte, "r", "g", "b", ushort, "a");
109 	}
110 	S s;
111 	static assert(is(typeof(s.r) == ubyte));
112 	static assert(is(typeof(s.g) == ubyte));
113 	static assert(is(typeof(s.b) == ubyte));
114 	static assert(is(typeof(s.a) == ushort));
115 }
116 
117 /// Return true if all of T's fields are the same type.
118 @property bool isHomogenous(T)()
119 {
120 	foreach (i, f; T.init.tupleof)
121 		if (!is(typeof(T.init.tupleof[i]) == typeof(T.init.tupleof[0])))
122 			return false;
123 	return true;
124 }
125 
126 template isValueOfTypeInTuple(X, T...)
127 {
128 	static if (T.length==0)
129 		enum bool isValueOfTypeInTuple = false;
130 	else
131 	static if (T.length==1)
132 		enum bool isValueOfTypeInTuple = is(typeof(T[0]) : X);
133 	else
134 		enum bool isValueOfTypeInTuple = isValueOfTypeInTuple!(X, T[0..$/2]) || isValueOfTypeInTuple!(X, T[$/2..$]);
135 }
136 
137 unittest
138 {
139 	static assert( isValueOfTypeInTuple!(int, ValueTuple!("a", 42)));
140 	static assert(!isValueOfTypeInTuple!(int, ValueTuple!("a", 42.42)));
141 	static assert(!isValueOfTypeInTuple!(int, ValueTuple!()));
142 
143 	static assert(!isValueOfTypeInTuple!(int, "a", int, Object));
144 	static assert( isValueOfTypeInTuple!(int, "a", int, Object, 42));
145 }
146 
147 template findValueOfTypeInTuple(X, T...)
148 {
149 	static if (T.length==0)
150 		static assert(false, "Can't find value of type " ~ X.stringof ~ " in specified tuple");
151 	else
152 	static if (is(typeof(T[0]) : X))
153 		enum findValueOfTypeInTuple = T[0];
154 	else
155 		enum findValueOfTypeInTuple = findValueOfTypeInTuple!(X, T[1..$]);
156 }
157 
158 unittest
159 {
160 	static assert(findValueOfTypeInTuple!(int, ValueTuple!("a", 42))==42);
161 	static assert(findValueOfTypeInTuple!(int, "a", int, Object, 42)==42);
162 }
163 
164 /// One past the biggest element of the enum T.
165 /// Example: string[EnumLength!E] arr;
166 template EnumLength(T)
167 	if (is(T==enum))
168 {
169 	enum EnumLength = cast(T)(cast(size_t)T.max + 1);
170 }
171 
172 // ************************************************************************
173 
174 // http://d.puremagic.com/issues/show_bug.cgi?id=7805
175 static template stringofArray(Args...)
176 {
177 	static string[] stringofArray()
178 	{
179 		string[] args;
180 		foreach (i, _ ; typeof(Args))
181 			args ~= Args[i].stringof;
182 		return args;
183 	}
184 }
185 
186 /// Returns the index of fun's parameter with the name
187 /// matching "names", or asserts if the parameter is not found.
188 /// "names" can contain multiple names separated by slashes.
189 static size_t findParameter(alias fun, string names)()
190 {
191 	foreach (name; names.split("/"))
192 		foreach (i, param; ParameterIdentifierTuple!fun)
193 			if (param == name)
194 				return i;
195 	assert(false, "Function " ~ __traits(identifier, fun) ~ " doesn't have a parameter called " ~ name);
196 }
197 
198 /// ditto
199 // Workaround for no "static alias" template parameters
200 static size_t findParameter()(string[] searchedNames, string soughtNames, string funName)
201 {
202 	foreach (soughtName; soughtNames.split("/"))
203 	{
204 		auto targetIndex = searchedNames.countUntil(soughtName);
205 		if (targetIndex >= 0)
206 			return targetIndex;
207 	}
208 	assert(false, "No argument %s in %s's parameters (%s)".format(soughtNames, funName, searchedNames).idup);
209 }
210 
211 // ************************************************************************
212 
213 // Using a compiler with UDA support?
214 enum HAVE_UDA = __traits(compiles, __traits(getAttributes, Object));
215 
216 static if (HAVE_UDA)
217 {
218 	template hasAttribute(T, alias D)
219 	{
220 		enum bool hasAttribute = isValueOfTypeInTuple!(T, __traits(getAttributes, D));
221 	}
222 
223 	template getAttribute(T, alias D)
224 	{
225 		enum T getAttribute = findValueOfTypeInTuple!(T, __traits(getAttributes, D));
226 	}
227 }
228 else
229 {
230 	template hasAttribute(T, alias D)
231 	{
232 		enum bool hasAttribute = false;
233 	}
234 
235 	template getAttribute(T, alias D)
236 	{
237 		static assert(false, "This D compiler has no UDA support.");
238 	}
239 }
240 
241 // ************************************************************************
242 
243 import std.conv;
244 import std.string;
245 
246 string mixGenerateContructorProxies(T)()
247 {
248 	string s;
249 	static if (__traits(hasMember, T, "__ctor"))
250 		foreach (ctor; __traits(getOverloads, T, "__ctor"))
251 		{
252 			string[] declarationList, usageList;
253 			foreach (i, param; ParameterTypeTuple!(typeof(&ctor)))
254 			{
255 				auto varName = "v" ~ text(i);
256 				declarationList ~= param.stringof ~ " " ~ varName;
257 				usageList ~= varName;
258 			}
259 			s ~= "this(" ~ declarationList.join(", ") ~ ") { super(" ~ usageList.join(", ") ~ "); }\n";
260 		}
261 	return s;
262 }
263 
264 /// Generate constructors that simply call the parent class constructors.
265 /// Based on http://forum.dlang.org/post/i3hpj0$2vc6$1@digitalmars.com
266 mixin template GenerateContructorProxies()
267 {
268 	mixin(mixGenerateContructorProxies!(typeof(super))());
269 }
270 
271 unittest
272 {
273 	class A
274 	{
275 		int i, j;
276 		this() { }
277 		this(int i) { this.i = i; }
278 		this(int i, int j ) { this.i = i; this.j = j; }
279 	}
280 
281 	class B : A
282 	{
283 		mixin GenerateContructorProxies;
284 	}
285 
286 	A a;
287 
288 	a = new B();
289 	assert(a.i == 0);
290 	a = new B(17);
291 	assert(a.i == 17);
292 	a = new B(17, 42);
293 	assert(a.j == 42);
294 }
295 
296 // ************************************************************************
297 
298 /// Generate a @property function which creates/returns
299 /// a thread-local singleton of a class with the given arguments.
300 
301 @property T singleton(T, args...)()
302 	if (is(typeof(new T(args))))
303 {
304 	static T instance;
305 	if (!instance)
306 		instance = new T(args);
307 	return instance;
308 }
309 
310 unittest
311 {
312 	static class C
313 	{
314 		static int n = 0;
315 
316 		this()      { n++; }
317 		this(int x) { n += x; }
318 
319 		void fun() {}
320 	}
321 
322 	alias singleton!C c0;
323 	c0.fun();
324 	c0.fun();
325 	assert(C.n == 1);
326 
327 	alias singleton!(C, 5) c1;
328 	c1.fun();
329 	c1.fun();
330 	assert(C.n == 6);
331 }
332 
333 // ************************************************************************
334 
335 /// Were we built with -debug?
336 debug
337 	enum isDebug = true;
338 else
339 	enum isDebug = false;
340 
341 deprecated alias IsDebug = isDebug;
342 
343 // ************************************************************************
344 
345 /// Shorter synonym for std.traits.Identity.
346 /// Can be used to UFCS-chain static methods and nested functions.
347 alias I(alias A) = A;
348 
349 /// Get f's ancestor which represents its "this" pointer.
350 /// Skips template and mixin ancestors until it finds a struct or class.
351 template thisOf(alias f)
352 {
353 	alias p = Identity!(__traits(parent, f));
354 	static if (is(p == class) || is(p == struct) || is(p == union))
355 		alias thisOf = p;
356 	else
357 		alias thisOf = thisOf!p;
358 }
359 
360 // ************************************************************************
361 
362 /// Unsigned integer type big enough to fit N bits of precision.
363 template UnsignedBitsType(uint bits)
364 {
365 	static if (bits <= 8)
366 		alias ubyte UnsignedBitsType;
367 	else
368 	static if (bits <= 16)
369 		alias ushort UnsignedBitsType;
370 	else
371 	static if (bits <= 32)
372 		alias uint UnsignedBitsType;
373 	else
374 	static if (bits <= 64)
375 		alias ulong UnsignedBitsType;
376 	else
377 		static assert(0, "No integer type big enough to fit " ~ bits.stringof ~ " bits");
378 }
379 
380 template SignedBitsType(uint bits)
381 {
382 	alias Signed!(UnsignedBitsType!bits) SignedBitsType;
383 }
384 
385 /// Evaluates to array of strings with name for each field.
386 @property string[] structFields(T)()
387 	if (is(T == struct) || is(T == class))
388 {
389 	import std.string : split;
390 
391 	string[] fields;
392 	foreach (i, f; T.init.tupleof)
393 	{
394 		string field = T.tupleof[i].stringof;
395 		field = field.split(".")[$-1];
396 		fields ~= field;
397 	}
398 	return fields;
399 }