1 /** Various stuff for working with _templates.
2 
3 Copyright: Denis Shelomovskij 2011-2012
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Denis Shelomovskij
8 */
9 module unstd.templates;
10 
11 
12 import std.ascii: isDigit;
13 
14 import unstd.generictuple;
15 import unstd.traits;
16 
17 
18 /**
19 Instantiate a template $(D Template) using arguments $(D A).
20 */
21 alias Inst(alias Template, A...) = Template!A;
22 
23 ///
24 unittest
25 {
26 	import std.traits: PointerTarget;
27 
28 	static assert(is(Inst!(PointerTarget, int*) == int));
29 }
30 
31 unittest
32 {
33 	import std.traits: isPointer;
34 	static assert( Inst!(isPointer, int*));
35 	static assert(!Inst!(isPointer, int ));
36 	static assert(Inst!(GenericTuple, 5)[0] == 5);
37 }
38 
39 
40 /**
41 Create template from a string $(D Pred).
42 If $(D Pred) isn't a string, alises itself to $(D Pred).
43 
44 If $(D argumentsCount) is $(D -1) created template will accept any number
45 of arguments, otherwise it will expect $(D argumentsCount) arguments.
46 
47 If $(D EnumType) is $(D void) created template may be an $(D alias) or
48 an $(D enum), otherwise it will be an $(D enum) of type $(D EnumType).
49 
50 Created template can access its aruments as a generic tuple with $(D Args).
51 
52 If $(D argumentsCount) is $(D 1) or $(D 2) created template can access
53 its first argument with $(D a) if it is an value, with $(D T) if it is a type
54 and with $(D A) otherwise.
55 
56 If $(D argumentsCount) is $(D 2) created template can access
57 its second argument with $(D b) if it is an value, with $(D U) if it is a type
58 and with $(D B) otherwise.
59 
60 $(D UnaryTemplate) is a convinient way to create a template with one argument ($(D argumentsCount) is $(D 1)).
61 
62 $(D BinaryTemplate) is a convinient way to create a template with two arguments ($(D argumentsCount) is $(D 2)).
63 */
64 template Template(alias Pred, int argumentsCount, EnumType = void)
65 	if(argumentsCount >= -1)
66 {
67 	static if(isSomeString!(typeof(Pred)))
68 	{
69 		template Template(Args...) if(argumentsCount == -1 || Args.length == argumentsCount)
70 		{
71 			static if(argumentsCount >= 1 && argumentsCount <= 2)
72 			{
73 				static if(__traits(compiles, { enum e = Args[0]; }))
74 					enum a = Args[0];
75 				else static if(is(Args[0]))
76 					alias T = Args[0];
77 				else
78 					alias A = Args[0];
79 
80 				static if(argumentsCount == 2)
81 				{
82 					static if(__traits(compiles, { enum e = Args[1]; }))
83 						enum b = Args[1];
84 					else static if(is(Args[1]))
85 						alias U = Args[1];
86 					else
87 						alias B = Args[1];
88 				}
89 			}
90 
91 			static if(is(EnumType == void))
92 			{
93 				static if(__traits(compiles, { enum e = mixin(Pred); }))
94 					enum Template = mixin(Pred);
95 				else
96 					mixin(`alias Template = `~Pred~`;`);
97 			}
98 			else
99 			{
100 				enum EnumType Template = mixin(Pred);
101 			}
102 		}
103 	} else
104 		alias Template = Pred;
105 }
106 
107 /// ditto
108 alias UnaryTemplate(alias Pred, EnumType = void) = Template!(Pred, 1, EnumType);
109 
110 /// ditto
111 alias BinaryTemplate(alias Pred, EnumType = void) = Template!(Pred, 2, EnumType);
112 
113 ///
114 unittest
115 {
116 	static assert(Inst!(UnaryTemplate!`__traits(isUnsigned, T)`, uint));
117 	static assert(is(Inst!(UnaryTemplate!`T[]`, int) == int[]));
118 	static assert(Inst!(UnaryTemplate!`a == 5`, 5));
119 	static assert(Inst!(BinaryTemplate!`a == 1 && b == 2`, 1, 2));
120 	static assert(Inst!(BinaryTemplate!`a + U.sizeof`, 1, int) == 5);
121 	static assert(PackedGenericTuple!(Inst!(Template!(`Args`, -1), "x", int)).equals!("x", int));
122 }
123 
124 unittest
125 {
126 	static assert(Inst!(UnaryTemplate!`!__traits(isUnsigned, T)`,  int));
127 	static assert(Inst!(Inst!(UnaryTemplate!notTemplate, isPointer), int));
128 	static assert(Inst!(Inst!(UnaryTemplate!`notTemplate!A`, isPointer), int));
129 	static assert(Inst!(Inst!(UnaryTemplate!(notTemplate!notTemplate), isPointer), int*));
130 	static assert(Inst!(Inst!(UnaryTemplate!`Inst!(notTemplate!notTemplate, A)`, isPointer), int*));
131 
132 	static assert(Inst!(UnaryTemplate!`a == 7`w, 7));
133 
134 	static assert(!__traits(compiles, Inst!(Template!(`T`, bool), int)));
135 
136 	static assert(PackedGenericTuple!(Inst!(Template!(`Args`, -1), 1, int, "x")).equals!(1, int, "x"));
137 }
138 
139 
140 /**
141 Using $(D unaryPred) or $(D binaryPred) is a convinient way to create a template
142 with one or two arguments respectively which is an $(D enum) of type $(D bool).
143 
144 It is equal to instantiating $(MREF Template) with corresponding
145 $(D argumentsCount) and $(D bool) as $(D EnumType).
146 */
147 alias unaryPred(alias pred) = UnaryTemplate!(pred, bool);
148 
149 /// ditto
150 alias binaryPred(alias pred) = BinaryTemplate!(pred, bool);
151 
152 ///
153 unittest
154 {
155 	static assert(Inst!(unaryPred!`__traits(isUnsigned, T)`, uint));
156 	static assert(Inst!(binaryPred!`a == U.sizeof`, 4, int));
157 }
158 
159 unittest
160 {
161 	static assert(Inst!(unaryPred!`!__traits(isUnsigned, T)`,  int));
162 	static assert(Inst!(unaryPred!`a == 5`, 5));
163 	static assert(!__traits(compiles, Inst!(unaryPred!`T`, int)));
164 }
165 
166 
167 /**
168 Create predicate template returning $(D !template_).
169 */
170 template notTemplate(alias template_)
171 {
172 	template notTemplate(T...)
173 	{
174 		static if(__traits(compiles, { enum e = template_!T; }))
175 			enum bool notTemplate = !template_!T;
176 		else
177 			template notTemplate(U...)
178 			{
179 				enum bool notTemplate = !Inst!(template_!T, U);
180 			}
181 	}
182 }
183 
184 ///
185 unittest
186 {
187 	import std.traits: isPointer;
188 
189 	alias notPointer = notTemplate!isPointer;
190 	static assert( notPointer! int );
191 	static assert(!notPointer!(int*));
192 
193 	alias toBoolTemplate = notTemplate!notTemplate;
194 	static assert(Inst!(toBoolTemplate!isPointer, int*));
195 	template get5() { enum get5 = 5; }
196 	static assert(Inst!(toBoolTemplate!get5) == true);
197 }
198 
199 unittest
200 {
201 	alias notPointer = notTemplate!isPointer;
202 	static assert( notPointer! int );
203 	static assert(!notPointer!(int*));
204 	static assert( Inst!(notTemplate!isPointer, int ));
205 	static assert(!Inst!(notTemplate!isPointer, int*));
206 
207 	alias toBoolTemplate = notTemplate!notTemplate;
208 
209 	alias _isPointer = toBoolTemplate!isPointer;
210 	static assert(!_isPointer! int );
211 	static assert( _isPointer!(int*));
212 
213 	alias get5 = Template!("5", 0);
214 	static assert(Inst!(toBoolTemplate!get5) == true);
215 }
216 
217 
218 /**
219 Create predicate template returning $(D true) iff there are no templates or all
220 $(D templates) return non-zero.
221 */
222 template andTemplates(templates...)
223 {
224 	template andTemplates(T...)
225 	{
226 		static if(templates.length == 0)
227 			enum andTemplates = true;
228 		else static if(Inst!(templates[0], T))
229 			enum andTemplates = Inst!(.andTemplates!(templates[1 .. $]), T);
230 		else
231 			enum andTemplates = false;
232 	}
233 }
234 
235 ///
236 unittest
237 {
238 	import std.traits: isIntegral, isSigned;
239 
240 	alias isSignedIntegral = andTemplates!(isIntegral, isSigned);
241 	static assert( allTuple!(isSignedIntegral,  int,  short, long));
242 	static assert(!anyTuple!(isSignedIntegral, uint, ushort, ulong));
243 
244 	alias isShort = andTemplates!(isSignedIntegral, unaryPred!`is(T == short)`);
245 	static assert( isShort!short);
246 	static assert(!anyTuple!(isShort, int, long, uint, ushort, ulong));
247 }
248 
249 unittest
250 {
251 	alias _true = andTemplates!();
252 	static assert(_true!() && _true!int && _true!(int, int*));
253 
254 	import std.traits: isPointer;
255 
256 	alias _isPointer = andTemplates!isPointer;
257 	static assert(_isPointer!(int*) && !_isPointer!int);
258 }
259 
260 
261 /**
262 Create predicate template returning $(D true) iff any template of
263 $(D templates) return non-zero (i.e. returning $(D false) if there
264 are no templates).
265 */
266 template orTemplates(templates...)
267 {
268 	template orTemplates(T...)
269 	{
270 		static if(templates.length == 0)
271 			enum orTemplates = false;
272 		else static if(!Inst!(templates[0], T))
273 			enum orTemplates = Inst!(.orTemplates!(templates[1 .. $]), T);
274 		else
275 			enum orTemplates = true;
276 	}
277 }
278 
279 ///
280 unittest
281 {
282 	import std.traits: isIntegral, isFloatingPoint;
283 
284 	alias isIntegralOrFloating = orTemplates!(isIntegral, isFloatingPoint);
285 	static assert( allTuple!(isIntegralOrFloating, int,  short, long, float, double));
286 	static assert(!anyTuple!(isIntegralOrFloating, bool, char));
287 
288 	alias isIntegralOrFloatingOrChar = orTemplates!(isIntegralOrFloating, unaryPred!`is(T == char)`);
289 	static assert( allTuple!(isIntegralOrFloatingOrChar, int, short, long, float, double, char));
290 	static assert(!isIntegralOrFloatingOrChar!bool);
291 }
292 
293 unittest
294 {
295 	alias _false = orTemplates!();
296 	static assert(!_false!() && !_false!int && !_false!(int, int*));
297 
298 	import std.traits: isPointer;
299 
300 	alias _isPointer = orTemplates!isPointer;
301 	static assert(_isPointer!(int*) && !_isPointer!int);
302 }
303 
304 
305 /**
306 Binds template arguments.
307 
308 $(UL
309 	$(LI use $(D args[i]) or $(D arg!i) to refer $(D i)-th argument;)
310 	$(LI use $(D args[$ - i]) to refer arguments from the end
311 		(also $(D args[$ + i]) can be used for negative $(D i));)
312 	$(LI use $(D args[a .. b]) or $(D argsRange!(a, b)) to refer arguments from
313 		$(D a)-th up to and excluding $(D b)-th;)
314 	$(LI use $(D argsToEnd!n) to refer arguments from $(D n)-th argument up to
315 		the end;)
316 	$(LI use $(D allArgs) to refer all arguments.)
317 )
318 
319 Example:
320 ----
321 import unstd.traits;
322 
323 static assert(is(Inst!(BindTemplate!(CommonType, long, allArgs), int) == long));
324 static assert(!Inst!(BindTemplate!(isImplicitlyConvertible, args[0], int), long));
325 static assert( Inst!(BindTemplate!(isImplicitlyConvertible, int  , arg!0), long));
326 
327 alias UnqualAll = BindTemplate!(MapTuple, Unqual, allArgs);
328 static assert(is(UnqualAll!(const(int), immutable(bool[])) == TypeTuple!(int, immutable(bool)[])));
329 ----
330 
331 Bugs:
332 Currently there is no support for $(D args[a .. $]) because of compiler limitations.
333 */
334 template BindTemplate(alias Template, BindArgs...)
335 {
336 	alias BindTemplate(Args...) =
337 		Template!(TemplateBindArgs!(BindArgs.length, BindArgs, Args));
338 }
339 
340 // Note: unittest can't be used as an example here as there is no way to place it before `Bugs` section.
341 
342 private struct __BindArgs
343 {
344 	struct Arg { size_t n; }
345 	struct ArgsRange { size_t from, to; }
346 	struct ArgsToEnd { size_t from; }
347 	struct ArgDollar
348 	{
349 		int sub = 0;
350 		auto opBinary(string op)(int n) const
351 			if(op == "+" || op == "-")
352 		{ return ArgDollar(op == "+" ? -n : n); }
353 	}
354 
355 	auto opDollar() const { return ArgDollar(); }
356 	auto opIndex(size_t n)               const { return Arg(n); }
357 	auto opIndex(ArgDollar d)            const { return d; }
358 	auto opSlice(size_t from, size_t to) const { return ArgsRange(from, to); }
359 	auto opSlice()                       const { return ArgsToEnd(0); }
360 }
361 
362 enum args = __BindArgs();
363 template arg(size_t n) { enum arg = __BindArgs.Arg(n); }
364 template argsRange(size_t from, size_t to) { enum argsRange = __BindArgs.ArgsRange(from, to); }
365 template argsToEnd(size_t from) { enum argsToEnd = __BindArgs.ArgsToEnd(from); }
366 enum allArgs = argsToEnd!0;
367 
368 private template TemplateBindArgs(size_t bindedCount, T...)
369 {
370 	static if(!bindedCount)
371 	{
372 		alias TemplateBindArgs = GenericTuple!();
373 	}
374 	else
375 	{
376 		alias Args = T[bindedCount .. $];
377 		alias Rest = TemplateBindArgs!(bindedCount-1, T[1..$]);
378 
379 		static if(is(typeof(T[0]) == __BindArgs.Arg))
380 		{
381 			alias TemplateBindArgs = GenericTuple!(Args[T[0].n], Rest);
382 		}
383 		else static if(is(typeof(T[0]) == __BindArgs.ArgDollar))
384 		{
385 			alias TemplateBindArgs = GenericTuple!(Args[Args.length - T[0].sub], Rest);
386 		}
387 		else static if(is(typeof(T[0]) == __BindArgs.ArgsRange))
388 		{
389 			alias TemplateBindArgs = GenericTuple!(Args[T[0].from .. T[0].to], Rest);
390 		}
391 		else static if(is(typeof(T[0]) == __BindArgs.ArgsToEnd))
392 		{
393 			alias TemplateBindArgs = GenericTuple!(Args[T[0].from .. $], Rest);
394 		}
395 		else
396 		{
397 			alias TemplateBindArgs = GenericTuple!(T[0], Rest);
398 		}
399 	}
400 }
401 
402 unittest
403 {
404 	alias Pack = PackedGenericTuple;
405 	static assert(Pack!(Inst!(BindTemplate!(GenericTuple, 1, 2, int), 3)).equals!(1, 2, int));
406 	static assert(Inst!(BindTemplate!(GenericTuple, arg!0), 3) == expressionTuple!3);
407 	static assert(Pack!(Inst!(BindTemplate!(GenericTuple, 1, 2, int, args[0]), 3)).equals!(1, 2, int, 3));
408 	static assert(Pack!(Inst!(BindTemplate!(GenericTuple, 1, 2, int, allArgs), 3)).equals!(1, 2, int, 3));
409 	static assert(Pack!(Inst!(BindTemplate!(GenericTuple,
410 			1, args[0 .. 1], 2, int, args[$ - 1]
411 		),
412 			3
413 		)).equals!(
414 			1, 3, 2, int, 3));
415 	static assert(Pack!(Inst!(BindTemplate!(GenericTuple,
416 			1, arg!1, 2, arg!0, int, args[$ + -3], allArgs,
417 		),
418 			3, char, 5
419 		)).equals!(
420 			1, char, 2, 3, int, 3, 3, char, 5));
421 
422 	import unstd.traits;
423 
424 	static assert(is(Inst!(BindTemplate!(CommonType, long, allArgs), int) == long));
425 	static assert(!Inst!(BindTemplate!(isImplicitlyConvertible, args[0], int), long));
426 	static assert( Inst!(BindTemplate!(isImplicitlyConvertible, int  , arg!0), long));
427 
428 	alias UnqualAll = BindTemplate!(MapTuple, Unqual, allArgs);
429 	static assert(is(UnqualAll!(const(int), immutable(bool[])) == TypeTuple!(int, immutable(bool)[])));
430 }
431 
432 // allArgs -> %*, arg! -> %
433 string formatBind(string fmt)
434 {
435 	string res, id;
436 
437 	int state = 0;
438 	foreach(i, char c; fmt)
439 	{
440 		if(state == 0 && c == '=')
441 		{
442 			id = fmt[0 .. i];
443 		}
444 		else if(state == 0 && c == '!')
445 		{
446 			res = fmt[id ? id.length + 1 : 0 .. i];
447 			res ~= ',';
448 			state = 1;
449 		}
450 		else if(state == 1 && c == '(')
451 		{
452 			fmt = fmt[i + 1 .. $];
453 			state = 2;
454 			break;
455 		}
456 	}
457 	assert(state == 2, "invalid format string, can't find '!(': '" ~ fmt ~ "'");
458 
459 
460 	foreach_reverse(i, char c; fmt)
461 		if(c == ')')
462 		{
463 			if(!id)
464 				id = fmt[i + 1 .. $];
465 			fmt = fmt[0 .. i];
466 			state = 3;
467 			break;
468 		}
469 	assert(state == 3, "invalid format string, can't find ')': " ~ fmt ~ "'");
470 
471 
472 	bool ctrl = false;
473 	size_t start = 0;
474 	foreach(i, char c; fmt)
475 	{
476 		if(ctrl)
477 		{
478 			ctrl = false;
479 			if(c == '%') // %% -> %
480 			{ }
481 			else if(c == '*') // %* -> allArgs
482 			{
483 				res ~= "allArgs";
484 				++start;
485 			}
486 			else if(c.isDigit()) // %# -> arg!#
487 				res ~= "arg!";
488 		}
489 		else if(c == '%')
490 		{
491 			res ~= fmt[start .. i];
492 			start = i + 1;
493 			ctrl = true;
494 		}
495 	}
496 	assert(!ctrl, "'%' at end of format string: '" ~ fmt ~ "'");
497 
498 
499 	res ~= fmt[start .. $];
500 	res = id ~ " = BindTemplate!(" ~ res ~ ")";
501 	return res;
502 }
503 
504 unittest
505 {
506 	enum beg = "x = BindTemplate!(";
507 	static assert(formatBind("x=ab!()") == beg ~ "ab,)");
508 	static assert(formatBind("x = ab !()") == "x  = BindTemplate!( ab ,)");
509 	static assert(formatBind("ab!()x") == beg ~ "ab,)");
510 	static assert(formatBind("ab !() x") == " x = BindTemplate!(ab ,)");
511 	static assert(formatBind("ab ! ()x") == beg ~ "ab ,)");
512 	static assert(formatBind("t!(ab%%c)x") == beg ~ "t,ab%c)");
513 	static assert(formatBind("t!(ab%%)x") == beg ~ "t,ab%)");
514 	static assert(formatBind("t!(ab%0c)x") == beg ~ "t,abarg!0c)");
515 	static assert(formatBind("t!(ab%10)x") == beg ~ "t,abarg!10)");
516 	static assert(formatBind("t!(ab%0)x") == beg ~ "t,abarg!0)");
517 	static assert(formatBind("t!(ab%0c%1d)x") == beg ~ "t,abarg!0carg!1d)");
518 }
519 
520 /**
521 Binds template arguments using format string.
522 
523 $(UL
524 	$(LI use $(D %i) to refer $(D i)-th argument;)
525 	$(LI use $(D %*) to refer all arguments;)
526 	$(LI use $(D %%) for a $(D %) symbol.)
527 )
528 */
529 mixin template Bind(string fmt)
530 {
531 	mixin("alias " ~ fmt.formatBind() ~ ";");
532 }
533 
534 ///
535 unittest
536 {
537 	import unstd.traits;
538 
539 	mixin Bind!q{ CommonTypeToLong = CommonType!(long, %*) };
540 	static assert(is(CommonTypeToLong!int == long));
541 
542 	mixin Bind!q{ isImplicitlyConvertibleToInt = isImplicitlyConvertible!(%0, int) };
543 	static assert(!isImplicitlyConvertibleToInt!long);
544 
545 	mixin Bind!q{ isImplicitlyConvertibleFromInt = isImplicitlyConvertible!(int, %0) };
546 	static assert( isImplicitlyConvertibleFromInt!long);
547 
548 	mixin Bind!q{ UnqualAll = MapTuple!(Unqual, %*) };
549 	static assert(is(UnqualAll!(const(int), immutable(bool[])) == TypeTuple!(int, immutable(bool)[])));
550 }
551 
552 unittest
553 {
554 	void test(string fmt, size_t n, Args...)()
555 	{
556 		mixin Bind!("Res0 = " ~ fmt);
557 		static assert(Pack!(Res0!(Args[0 .. n])).equals!(Args[n .. $]));
558 
559 		mixin Bind!(fmt ~ " Res");
560 		static assert(Pack!(Res!(Args[0 .. n])).equals!(Args[n .. $]));
561 	}
562 
563 	alias Pack = PackedGenericTuple;
564 	static assert(Pack!(GenericTuple!(1, 2, int)).equals!(1, 2, int));
565 	test!(
566 		q{ GenericTuple!(1, 2, int) },
567 		1, 3,
568 		1, 2, int
569 	)();
570 	test!(
571 		q{ GenericTuple!(%0) },
572 		1, 3,
573 		3
574 	)();
575 	test!(
576 		q{ GenericTuple!(1, 2, int, %0) },
577 		1, 3,
578 		1, 2, int, 3
579 	)();
580 	test!(
581 		q{ GenericTuple!(1, 2, int, %*) },
582 		1, 3,
583 		1, 2, int, 3
584 	)();
585 	test!(
586 		q{ GenericTuple!(1, %0, 2, int, %0) },
587 		1, 3,
588 		1, 3, 2, int, 3
589 	)();
590 	test!(
591 		q{ GenericTuple!(1, %1, 2, %0, int, %0, %*,) },
592 		3, 3, char, 5,
593 		1, char, 2, 3, int, 3, 3, char, 5
594 	)();
595 }