1 /**
2 	Extensions to `std.traits` module of Phobos. Some may eventually make it into Phobos,
3 	some are dirty hacks that work only for vibe.d
4 
5 	Copyright: © 2012 Sönke Ludwig
6 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
7 	Authors: Sönke Ludwig, Михаил Страшун
8 */
9 
10 module dutils.data.utils.traits;
11 
12 /**
13 	Checks if given type is a getter function type
14 
15 	Returns: `true` if argument is a getter
16  */
17 template isPropertyGetter(T...) if (T.length == 1) {
18 	import std.traits : functionAttributes, FunctionAttribute, ReturnType, isSomeFunction;
19 
20 	static if (isSomeFunction!(T[0])) {
21 		enum isPropertyGetter = (functionAttributes!(T[0]) & FunctionAttribute.property) != 0
22 			&& !is(ReturnType!T == void);
23 	} else
24 		enum isPropertyGetter = false;
25 }
26 
27 ///
28 unittest {
29 	interface Test {
30 		@property int getter();
31 		@property void setter(int);
32 		int simple();
33 	}
34 
35 	static assert(isPropertyGetter!(typeof(&Test.getter)));
36 	static assert(!isPropertyGetter!(typeof(&Test.setter)));
37 	static assert(!isPropertyGetter!(typeof(&Test.simple)));
38 	static assert(!isPropertyGetter!int);
39 }
40 
41 /**
42 	Checks if given type is a setter function type
43 
44 	Returns: `true` if argument is a setter
45  */
46 template isPropertySetter(T...) if (T.length == 1) {
47 	import std.traits : functionAttributes, FunctionAttribute, ReturnType, isSomeFunction;
48 
49 	static if (isSomeFunction!(T[0])) {
50 		enum isPropertySetter = (functionAttributes!(T) & FunctionAttribute.property) != 0
51 			&& is(ReturnType!(T[0]) == void);
52 	} else
53 		enum isPropertySetter = false;
54 }
55 
56 ///
57 unittest {
58 	interface Test {
59 		@property int getter();
60 		@property void setter(int);
61 		int simple();
62 	}
63 
64 	static assert(isPropertySetter!(typeof(&Test.setter)));
65 	static assert(!isPropertySetter!(typeof(&Test.getter)));
66 	static assert(!isPropertySetter!(typeof(&Test.simple)));
67 	static assert(!isPropertySetter!int);
68 }
69 
70 /**
71 	Deduces single base interface for a type. Multiple interfaces
72 	will result in compile-time error.
73 
74 	Params:
75 		T = interface or class type
76 
77 	Returns:
78 		T if it is an interface. If T is a class, interface it implements.
79 */
80 template baseInterface(T) if (is(T == interface) || is(T == class)) {
81 	import std.traits : InterfacesTuple;
82 
83 	static if (is(T == interface)) {
84 		alias baseInterface = T;
85 	} else {
86 		alias Ifaces = InterfacesTuple!T;
87 		static assert(Ifaces.length == 1,
88 				"Type must be either provided as an interface or implement only one interface");
89 		alias baseInterface = Ifaces[0];
90 	}
91 }
92 
93 ///
94 unittest {
95 	interface I1 {
96 	}
97 
98 	class A : I1 {
99 	}
100 
101 	interface I2 {
102 	}
103 
104 	class B : I1, I2 {
105 	}
106 
107 	static assert(is(baseInterface!I1 == I1));
108 	static assert(is(baseInterface!A == I1));
109 	static assert(!is(typeof(baseInterface!B)));
110 }
111 
112 /**
113 	Determins if a member is a public, non-static data field.
114 */
115 template isRWPlainField(T, string M) {
116 	static if (!isRWField!(T, M))
117 		enum isRWPlainField = false;
118 	else {
119 		//pragma(msg, T.stringof~"."~M~":"~typeof(__traits(getMember, T, M)).stringof);
120 		enum isRWPlainField = __traits(compiles, *(&__traits(getMember,
121 					Tgen!T(), M)) = *(&__traits(getMember, Tgen!T(), M)));
122 	}
123 }
124 
125 /**
126 	Determines if a member is a public, non-static, de-facto data field.
127 
128 	In addition to plain data fields, R/W properties are also accepted.
129 */
130 template isRWField(T, string M) {
131 	import std.traits;
132 	import std.typetuple;
133 
134 	static void testAssign()() {
135 		static if (!isCopyable!T)
136 			T t; // for structs with disabled copy constructor
137 		else
138 			T t = *(cast(T*) 0);
139 		__traits(getMember, t, M) = __traits(getMember, t, M);
140 	}
141 
142 	// reject type aliases
143 	static if (is(TypeTuple!(__traits(getMember, T, M))))
144 		enum isRWField = false;
145 	// reject non-public members
146 	else static if (!isPublicMember!(T, M))
147 		enum isRWField = false;
148 	// reject static members
149 	else static if (!isNonStaticMember!(T, M))
150 		enum isRWField = false;
151 	// reject non-typed members
152 	else static if (!is(typeof(__traits(getMember, T, M))))
153 		enum isRWField = false;
154 	// reject void typed members (includes templates)
155 	else static if (is(typeof(__traits(getMember, T, M)) == void))
156 		enum isRWField = false;
157 	// reject non-assignable members
158 	else static if (!__traits(compiles, testAssign!()()))
159 		enum isRWField = false;
160 	else static if (anySatisfy!(isSomeFunction, __traits(getMember, T, M))) {
161 		// If M is a function, reject if not @property or returns by ref
162 		private enum FA = functionAttributes!(__traits(getMember, T, M));
163 		enum isRWField = (FA & FunctionAttribute.property) != 0;
164 	} else {
165 		enum isRWField = true;
166 	}
167 }
168 
169 unittest {
170 	import std.algorithm;
171 
172 	static struct S {
173 		alias a = int; // alias
174 		int i; // plain RW field
175 		enum j = 42; // manifest constant
176 		static int k = 42; // static field
177 		private int privateJ; // private RW field
178 
179 		this(Args...)(Args args) {
180 		}
181 
182 		@disable this(this);
183 
184 		// read-write property (OK)
185 		@property int p1() {
186 			return privateJ;
187 		}
188 
189 		@property void p1(int j) {
190 			privateJ = j;
191 		}
192 		// read-only property (NO)
193 		@property int p2() {
194 			return privateJ;
195 		}
196 		// write-only property (NO)
197 		@property void p3(int value) {
198 			privateJ = value;
199 		}
200 		// ref returning property (OK)
201 		@property ref int p4() {
202 			return i;
203 		}
204 		// parameter-less template property (OK)
205 		@property ref int p5()() {
206 			return i;
207 		}
208 		// not treated as a property by DMD, so not a field
209 		@property int p6()() {
210 			return privateJ;
211 		}
212 
213 		@property void p6(int j)() {
214 			privateJ = j;
215 		}
216 
217 		static @property int p7() {
218 			return k;
219 		}
220 
221 		static @property void p7(int value) {
222 			k = value;
223 		}
224 
225 		ref int f1() {
226 			return i;
227 		} // ref returning function (no field)
228 
229 		int f2(Args...)(Args args) {
230 			return i;
231 		}
232 
233 		ref int f3(Args...)(Args args) {
234 			return i;
235 		}
236 
237 		void someMethod() {
238 		}
239 
240 		ref int someTempl()() {
241 			return i;
242 		}
243 	}
244 
245 	enum plainFields = ["i"];
246 	enum fields = ["i", "p1", "p4", "p5"];
247 
248 	foreach (mem; __traits(allMembers, S)) {
249 		static if (isRWField!(S, mem))
250 			static assert(fields.canFind(mem), mem ~ " detected as field.");
251 		else
252 			static assert(!fields.canFind(mem), mem ~ " not detected as field.");
253 
254 		static if (isRWPlainField!(S, mem))
255 			static assert(plainFields.canFind(mem), mem ~ " not detected as plain field.");
256 		else
257 			static assert(!plainFields.canFind(mem), mem ~ " not detected as plain field.");
258 	}
259 }
260 
261 package T Tgen(T)() {
262 	return T.init;
263 }
264 
265 /**
266 	Tests if the protection of a member is public.
267 */
268 template isPublicMember(T, string M) {
269 	import std.algorithm, std.typetuple : TypeTuple;
270 
271 	static if (!__traits(compiles, TypeTuple!(__traits(getMember, T, M))))
272 		enum isPublicMember = false;
273 	else {
274 		alias MEM = TypeTuple!(__traits(getMember, T, M));
275 		static if (__traits(compiles, __traits(getProtection, MEM)))
276 			enum isPublicMember = __traits(getProtection, MEM).among("public", "export");
277 		else
278 			enum isPublicMember = true;
279 	}
280 }
281 
282 unittest {
283 	class C {
284 		int a;
285 		export int b;
286 		protected int c;
287 		private int d;
288 		package int e;
289 		void f() {
290 		}
291 
292 		static void g() {
293 		}
294 
295 		private void h() {
296 		}
297 
298 		private static void i() {
299 		}
300 	}
301 
302 	static assert(isPublicMember!(C, "a"));
303 	static assert(isPublicMember!(C, "b"));
304 	static assert(!isPublicMember!(C, "c"));
305 	static assert(!isPublicMember!(C, "d"));
306 	static assert(!isPublicMember!(C, "e"));
307 	static assert(isPublicMember!(C, "f"));
308 	static assert(isPublicMember!(C, "g"));
309 	static assert(!isPublicMember!(C, "h"));
310 	static assert(!isPublicMember!(C, "i"));
311 
312 	struct S {
313 		int a;
314 		export int b;
315 		private int d;
316 		package int e;
317 	}
318 
319 	static assert(isPublicMember!(S, "a"));
320 	static assert(isPublicMember!(S, "b"));
321 	static assert(!isPublicMember!(S, "d"));
322 	static assert(!isPublicMember!(S, "e"));
323 
324 	S s;
325 	s.a = 21;
326 	assert(s.a == 21);
327 }
328 
329 /**
330 	Tests if a member requires $(D this) to be used.
331 */
332 template isNonStaticMember(T, string M) {
333 	import std.typetuple;
334 	import std.traits;
335 
336 	alias MF = TypeTuple!(__traits(getMember, T, M));
337 	static if (M.length == 0) {
338 		enum isNonStaticMember = false;
339 	} else static if (anySatisfy!(isSomeFunction, MF)) {
340 		enum isNonStaticMember = !__traits(isStaticFunction, MF);
341 	} else {
342 		enum isNonStaticMember = !__traits(compiles, () {
343 				auto x = __traits(getMember, T, M);
344 			}());
345 	}
346 }
347 
348 unittest { // normal fields
349 	struct S {
350 		int a;
351 		static int b;
352 		enum c = 42;
353 		void f();
354 		static void g();
355 		ref int h() {
356 			return a;
357 		}
358 
359 		static ref int i() {
360 			return b;
361 		}
362 	}
363 
364 	static assert(isNonStaticMember!(S, "a"));
365 	static assert(!isNonStaticMember!(S, "b"));
366 	static assert(!isNonStaticMember!(S, "c"));
367 	static assert(isNonStaticMember!(S, "f"));
368 	static assert(!isNonStaticMember!(S, "g"));
369 	static assert(isNonStaticMember!(S, "h"));
370 	static assert(!isNonStaticMember!(S, "i"));
371 }
372 
373 unittest { // tuple fields
374 	struct S(T...) {
375 		T a;
376 		static T b;
377 	}
378 
379 	alias T = S!(int, float);
380 	auto p = T.b;
381 	static assert(isNonStaticMember!(T, "a"));
382 	static assert(!isNonStaticMember!(T, "b"));
383 
384 	alias U = S!();
385 	static assert(!isNonStaticMember!(U, "a"));
386 	static assert(!isNonStaticMember!(U, "b"));
387 }
388 
389 /**
390 	Tests if a Group of types is implicitly convertible to a Group of target types.
391 */
392 bool areConvertibleTo(alias TYPES, alias TARGET_TYPES)()
393 		if (isGroup!TYPES && isGroup!TARGET_TYPES) {
394 	static assert(TYPES.expand.length == TARGET_TYPES.expand.length);
395 	foreach (i, V; TYPES.expand)
396 		if (!is(V : TARGET_TYPES.expand[i]))
397 			return false;
398 	return true;
399 }
400 
401 /// Test if the type $(D DG) is a correct delegate for an opApply where the
402 /// key/index is of type $(D TKEY) and the value of type $(D TVALUE).
403 template isOpApplyDg(DG, TKEY, TVALUE) {
404 	import std.traits;
405 
406 	static if (is(DG == delegate) && is(ReturnType!DG : int)) {
407 		private alias PTT = ParameterTypeTuple!(DG);
408 		private alias PSCT = ParameterStorageClassTuple!(DG);
409 		private alias STC = ParameterStorageClass;
410 		// Just a value
411 		static if (PTT.length == 1) {
412 			enum isOpApplyDg = (is(PTT[0] == TVALUE));
413 		} else static if (PTT.length == 2) {
414 			enum isOpApplyDg = (is(PTT[0] == TKEY)) && (is(PTT[1] == TVALUE));
415 		} else
416 			enum isOpApplyDg = false;
417 	} else {
418 		enum isOpApplyDg = false;
419 	}
420 }
421 
422 unittest {
423 	static assert(isOpApplyDg!(int delegate(int, string), int, string));
424 	static assert(isOpApplyDg!(int delegate(ref int, ref string), int, string));
425 	static assert(isOpApplyDg!(int delegate(int, ref string), int, string));
426 	static assert(isOpApplyDg!(int delegate(ref int, string), int, string));
427 }
428 
429 // Synchronized statements are logically nothrow but dmd still marks them as throwing.
430 // DMD#4115, Druntime#1013, Druntime#1021, Phobos#2704
431 import core.sync.mutex : Mutex;
432 
433 enum synchronizedIsNothrow = __traits(compiles, (Mutex m) nothrow{
434 		synchronized (m) {
435 		}
436 	});
437 
438 template StripHeadConst(T) {
439 	static if (is(T == const(F), F))
440 		alias StripHeadConst = StripHeadConst!F;
441 	else static if (is(T == immutable(F), F))
442 		alias StripHeadConst = StripHeadConst!F;
443 	else static if (is(T == inout(F), F))
444 		alias StripHeadConst = StripHeadConst!F;
445 	else
446 		alias StripHeadConst = T;
447 }
448 
449 unittest {
450 	static assert(is(StripHeadConst!(int) == int));
451 	static assert(is(StripHeadConst!(const(int)) == int));
452 	static assert(is(StripHeadConst!(immutable(int)) == int));
453 	static assert(is(StripHeadConst!(const(immutable(int))) == int));
454 	static assert(is(StripHeadConst!(const(int[])) == const(int)[]));
455 }
456 
457 template derivedMethod(C, alias method) {
458 	import std.traits : FunctionTypeOf, MemberFunctionsTuple, ParameterTypeTuple;
459 	import std.meta : AliasSeq;
460 
461 	enum fname = __traits(identifier, method);
462 	alias overloads = MemberFunctionsTuple!(C, fname);
463 	alias PTypes = ParameterTypeTuple!method;
464 
465 	template impl(size_t i) {
466 		static if (i >= overloads.length)
467 			alias impl = AliasSeq!();
468 		else {
469 			alias FT = FunctionTypeOf!(overloads[i]);
470 			static if (__traits(compiles, FT(PTypes.init)))
471 				alias impl = overloads[i];
472 			else
473 				alias impl = impl!(i + 1);
474 		}
475 	}
476 
477 	alias derivedMethod = impl!0;
478 }
479 
480 template RecursiveFunctionAttributes(alias func) {
481 	import std.meta : AliasSeq, staticMap;
482 	import std.traits : BaseTypeTuple;
483 
484 	static if (is(AliasSeq!(__traits(parent, func))[0])) {
485 		alias C = AliasSeq!(__traits(parent, func))[0];
486 		template rimpl(T) {
487 			alias DF = derivedMethod!(T, func);
488 			static if (AliasSeq!(DF).length > 0)
489 				alias rimpl = RecursiveFunctionAttributes!DF;
490 			else
491 				alias rimpl = AliasSeq!();
492 		}
493 
494 		alias RecursiveFunctionAttributes = AliasSeq!(__traits(getAttributes,
495 				func), staticMap!(rimpl, BaseTypeTuple!C));
496 	} else {
497 		alias RecursiveFunctionAttributes = AliasSeq!(__traits(getAttributes, func));
498 	}
499 }
500 
501 unittest {
502 	interface I {
503 		@(1) void test();
504 	}
505 
506 	interface J {
507 		@(4) void test(int);
508 	}
509 
510 	class C : I, J {
511 		override @(2) void test() {
512 		}
513 
514 		override void test(int) {
515 		}
516 	}
517 
518 	class D : C {
519 		override @(3) void test() {
520 		}
521 	}
522 
523 	static assert([RecursiveFunctionAttributes!(D.test)] == [3, 2, 1]);
524 }