1 // SemiTwist Library
2 // Written in the D programming language.
3 
4 module semitwist.util.mixins;
5 
6 import std.conv;
7 import std.stdio;
8 import std.traits;
9 
10 import semitwist.util.all;
11 
12 /++
13 Useful in constructors for DRY.
14 
15 Usage:
16 ----
17 mixin(initMember("someVar"));
18 mixin(initMember("a", "b", "c"));
19 ----
20 
21 Turns Into:
22 ----
23 this.someVar = someVar;
24 this.a = a;
25 this.b = b;
26 this.c = c;
27 ----
28 +/
29 string initMember(string[] vars...)
30 {
31 	return initMemberX("this.%s = %s", vars);
32 }
33 
34 /++
35 Generic version of initMember.
36 
37 Usage:
38 ----
39 mixin(initMemberX("foo1.%s = foo2.%s", "someVar"));
40 mixin(initMemberX("this._%s = foo.%s", "a", "b", "c"));
41 ----
42 
43 Turns Into:
44 ----
45 foo1.someVar = foo2.someVar;
46 this._a = foo.a;
47 this._b = foo.b;
48 this._c = foo.c;
49 ----
50 +/
51 string initMemberX(string str, string[] vars...)
52 {
53 	//enum string initMemberX = ctfe_subMapJoin!string(str~";\n", "%s", /+templateArgsToStrings!(+/vars/+)+/);
54 	return ctfe_subMapJoin!string(str~";\n", "%s", vars);
55 }
56 
57 /++
58 Useful in copy constructors for DRY.
59 
60 Usage:
61 ----
62 class myClass
63 {
64 	// Declarations of 'someVar', 'a1', 'b', and 'c' here.
65 	this(myClass copyOf)
66 	{
67 		mixin(initMemberFrom("copyOf", "someVar"));
68 		mixin(initMemberFrom("copyOf", "a1", "b", "c"));
69 	}
70 }
71 ----
72 
73 Turns Into:
74 ----
75 class myClass
76 {
77 	// Declarations of 'someVar', 'a1', 'b', and 'c' here.
78 	this(myClass copyOf)
79 	{
80 		this.someVar = copyOf.someVar;
81 		this.a1 = copyOf.a1;
82 		this.b = copyOf.b;
83 		this.c = copyOf.c;
84 	}
85 }
86 ----
87 +/
88 string initMemberFrom(string from, string[] vars...)
89 {
90 	return initMemberX("this.%s = "~from~".%s", vars);
91 }
92 
93 string initMemberTo(string to, string[] vars...)
94 {
95 	return initMemberX(to~".%s = %s", vars);
96 }
97 
98 string initFrom(string from, string[] vars...)
99 {
100 	return initMemberX("%s = "~from~".%s", vars);
101 }
102 
103 /++
104 A DRY way to display an expression and its value to Stdout.
105 
106 Usage:
107 
108 ----
109 int myVar=100;
110 mixin(traceVal!("myVar"));
111 mixin(traceVal!("   myVar-1 "));
112 mixin(traceVal!("min(4,7)", "max(4,7)")); // from tango.math.Math
113 ----
114 
115 Outputs:
116 
117 ----
118 myVar: 100
119    myVar-1 : 99
120 min(4,7): 4
121 max(4,7): 7
122 ----
123 +/
124 
125 //TODO: Add ability to specify format (binary, hex, etc)
126 //TODO: Make nameLength work by using format (at runtime)
127 //      on data passed to writefln
128 //      (ie, align name/value)
129 //TODO: Messes up on "ctfe_repeat_test_日本語3"
130 alias writefln _semitwist_traceVal_writefln;
131 void _semitwist_traceVal_stdout_flush()
132 {
133 	stdout.flush();
134 }
135 template traceVal(values...)
136 {
137 	enum traceVal = traceVal!(false, values);
138 }
139 
140 template traceVal(bool useNewline, values...)
141 {
142 	static if(values.length == 0)
143 		enum traceVal = "_semitwist_traceVal_stdout_flush();";
144 	else
145 	{
146 		enum traceVal =
147 			"_semitwist_traceVal_writefln(\"%s:"~(useNewline?"\\n":" ")~"%s\", "~values[0].stringof~", "~unescapeDDQS(values[0].stringof)~");"
148 			~ traceVal!(useNewline, values[1..$]);
149 	}
150 }
151 
152 /++
153 Easy way to output file/line for debugging.
154 
155 Usage:
156 
157 ----
158 trace();
159 someFunc();
160 trace("Blah blah blah");
161 int x = *(cast(int*)null); // Dereference null
162 trace();
163 ----
164 
165 Example Output:
166 
167 ----
168 C:\path\file.d(1): trace
169 C:\path\file.d(3): Blah blah blah
170 {segfault!}
171 ----
172 +/
173 
174 void trace(string file=__FILE__, size_t line=__LINE__)(string msg="trace")
175 {
176 	writefln("%s(%s): %s", file, line, msg);
177 	stdout.flush();
178 }
179 
180 /++
181 Wraps a string mixin and displays the string at compile-time. Useful for debugging.
182 
183 Usage:
184 
185 ----
186 template defineFloat(string name)
187 { enum defineFloat = "float "~name~";"; }
188 string defineInt(string name, string value)
189 { return "int "~name~"="~value";"; }
190 
191 mixin(traceMixin!("defineFloat!", `"myFloat"`));
192 mixin(traceMixin!("defineInt!", `"myInt", 5`));
193 ----
194 
195 Turns Into:
196 
197 ----
198 template defineFloat(string name)
199 { enum defineFloat = "float "~name~";"; }
200 string defineInt(string name, string value)
201 { return "int "~name~"="~value";"; }
202 
203 float myFloat;
204 pragma(msg, "defineFloat:\n float myFloat;");
205 int myInt=5;
206 pragma(msg, "defineInt:\n int myInt=5;");
207 ----
208 
209 Compiler Output:
210 
211 ----
212 defineFloat:
213 float myFloat;
214 defineInt:
215 int myInt=5;
216 ----
217 +/
218 
219 template traceMixin(string name, string args)
220 {
221 	enum traceMixin = 
222 		`pragma(msg, "` ~ name ~ `: \n"~`~name~`(`~args~`));`~"\n"~
223 		"mixin("~name~"("~args~"));\n";
224 }
225 
226 /++
227 Compile-time version of traceVal.
228 
229 Only works for string values right now.
230 
231 Usage:
232 
233 ----
234 enum fooStr = "Hi";
235 enum fooStr2 = "Hi2";
236 mixin(traceValCT!("fooStr", "fooStr2"));
237 mixin(traceValCT!(`fooStr~" Joe"`));
238 
239 template fooTmpl
240 {
241 	enum fooTempl = "Hello World";
242 	mixin(traceValCT!(true, "fooTempl"));
243 }
244 ----
245 
246 Turns Into:
247 
248 ----
249 enum fooStr = "Hi";
250 enum fooStr2 = "Hi2";
251 pragma(msg, "fooStr: " ~ (fooStr));
252 pragma(msg, "fooStr2: " ~ (fooStr2));
253 pragma(msg, "fooStr~\" Joe\""~": " ~ (fooStr~" Joe"));
254 
255 template fooTmpl
256 {
257 	enum fooTempl = "Hello World";
258 	pragma(msg, "fooTempl:\n" ~ (fooTempl));
259 }
260 ----
261 
262 Compiler Output:
263 
264 ----
265 fooStr: Hi
266 fooStr2: Hi2
267 fooStr~" Joe": Hi Joe
268 fooTempl:
269 Hello World
270 ----
271 +/
272 
273 template traceValCT(values...)
274 {
275 	enum traceValCT = traceValCT!(false, values);
276 }
277 
278 template traceValCT(bool useNewline, values...)
279 {
280 	static if(values.length == 0)
281 	{
282 		enum traceValCT = "";
283 	}
284 	else
285 	{
286 		enum traceValCT =
287 			"pragma(msg, "~escapeDDQS(values[0])~"~\":"~(useNewline? "\\n":" ")~"\" ~ ("~values[0]~"));\n"~
288 			traceValCT!(useNewline, values[1..$]);
289 
290 		//pragma(msg, "traceValCT: " ~ traceValCT);
291 	}
292 }
293 
294 /// Part of a workaround for DMD Issues #5029 and #5030
295 string fixAATypeName(string str)
296 {
297 	enum prefix = "AssociativeArray!(";
298 	if(str.length > prefix.length && str[0..prefix.length] == prefix)
299 	{
300 		auto strippedStr = str[prefix.length..$-1];
301 		int nestLevel=0;
302 		size_t i;
303 		bool done=false;
304 		for(i=0; !done && i < strippedStr.length; i++)
305 		{
306 			switch(strippedStr[i])
307 			{
308 			case '(': nestLevel++; break;
309 			case ')': nestLevel--; break;
310 			case ',':
311 				if(nestLevel==0)
312 					done=true;
313 				break;
314 			default: break;
315 			}
316 		}
317 		i--;
318 		auto typeKey = strippedStr[0..i];
319 		auto typeVal = strippedStr[i+1..$];
320 		
321 		return typeVal~"["~typeKey~"]";
322 	}
323 	else
324 		return str;
325 }
326 
327 /++
328 Useful in class/struct declarations for DRY.
329 
330 Generates a public getter, private setter, and a hidden private var.
331 
332 Usage:
333 
334 ----
335 mixin(getter!(int, "myVar"));
336 mixin(getter!("protected", float, "someFloat", 2.5));
337 mixin(getter!(string, "str"));
338 ----
339 
340 Turns Into:
341 
342 ----
343 private int _myVar;
344 @property private int myVar(int _NEW_VAL_)
345 {
346 	_myVar = _NEW_VAL_;
347 	return _myVar;
348 }
349 @property public int myVar()
350 {
351 	return _myVar;
352 }
353 
354 protected float _someFloat = 2.5;
355 @property protected float someFloat(float _NEW_VAL_)
356 {
357 	_someFloat = _NEW_VAL_;
358 	return _someFloat;
359 }
360 @property public float someFloat()
361 {
362 	return _someFloat;
363 }
364 
365 private string _str;
366 @property private string str(string _NEW_VAL_)
367 {
368 	_str = _NEW_VAL_;
369 	return _str;
370 }
371 @property public string str()
372 {
373 	return _str;
374 }
375 ----
376 +/
377 template getter(varType, string name, varType initialValue=varType.init)
378 {
379 	static if(is(varType.init))
380 		enum getter = getter!("private", varType, name, initialValue);
381 	else
382 		enum getter = getter!("private", varType, name);
383 }
384 
385 template getter(string writeAccess, varType, string name, varType initialValue=varType.init)
386 {
387 	static if(is(varType.init))
388 	{
389 		enum getter =
390 			writeAccess~" "~fixAATypeName(varType.stringof)~" _"~name~(initialValue.stringof == varType.init.stringof ? "" : "=" ~ initialValue.stringof)~";\n"~
391 			"@property "~writeAccess~" "~fixAATypeName(varType.stringof)~" "~name~"("~fixAATypeName(varType.stringof)~" _NEW_VAL_) {_"~name~"=_NEW_VAL_;return _"~name~";}\n"~
392 			"@property public "~fixAATypeName(varType.stringof)~" "~name~"() {return _"~name~";}\n";
393 	}
394 	else
395 	{
396 		enum getter =
397 			writeAccess~" "~fixAATypeName(varType.stringof)~" _"~name~";\n"~
398 			"@property "~writeAccess~" "~fixAATypeName(varType.stringof)~" "~name~"("~fixAATypeName(varType.stringof)~" _NEW_VAL_) {_"~name~"=_NEW_VAL_;return _"~name~";}\n"~
399 			"@property public "~fixAATypeName(varType.stringof)~" "~name~"() {return _"~name~";}\n";
400 	}
401 }
402 
403 /++
404 Similar to "getter", but for values that are to be lazily generated and cached.
405 This is useful for values that are complex to generate, not always used, and
406 either never or infrequently change.
407 
408 The first time the getter is called, the generator function you have provided
409 is run, and it's return value is cached and returned. On subsequent calls to
410 the getter, the cached value is returned without the generator function being
411 called. The cache can be cleared, thus forcing the value to be re-generated
412 upon the next getter call, by setting "_myVarName_cached" to false.
413 
414 Example use-case: If you have a property created by getter() and want
415 to change the "get" from a trivial "return _blah" to a more involved function, 
416 you will most likely just simply switch from getter to getterLazy.
417 
418 Additional Info:
419 
420 If you don't want the value to ever be cached, just set "_myVarName_cached"
421 to false within your provided generator function.
422 
423 Usage:
424 
425 ----
426 mixin(getterLazy!(int, "myVar", `
427 	// Ordinarily, this function would be more complex
428 	return 7;
429 `));
430 
431 mixin(getterLazy!("protected", int, "myVar2", `return 7;`));
432 
433 mixin(getterLazy!(string, "str"));
434 private string _str_gen()
435 {
436 	return "Hello";
437 }
438 ----
439 
440 Turns Into:
441 
442 ----
443 private int _myVar;
444 private bool _myVar_cached = false;
445 @property public int myVar() {
446 	if(!_myVar_cached) {
447 		_myVar_cached = true;
448 		_myVar = _myVar_gen();
449 	}
450 	return _myVar;
451 }
452 private int _myVar_gen()
453 {
454 	// Ordinarily, this function would be more complex
455 	return 7;
456 }
457 
458 protected int _myVar2;
459 protected bool _myVar2_cached = false;
460 @property public int myVar2() {
461 	if(!_myVar2_cached) {
462 		_myVar2_cached = true;
463 		_myVar2 = _myVar2_gen();
464 	}
465 	return _myVar2;
466 }
467 protected int _myVar2_gen()
468 {
469 	return 7;
470 }
471 
472 private string _str;
473 private bool _str_cached = false;
474 @property public string str() {
475 	if(!_str_cached) {
476 		_str_cached = true;
477 		_str = customGenFunc();
478 	}
479 	return _str;
480 }
481 private string customGenFunc()
482 {
483 	return "Hello";
484 }
485 
486 ----
487 +/
488 //TODO? Merge with getter if reasonably possible
489 template getterLazy(varType, string name, string genFunc="")
490 {
491 	enum getterLazy = getterLazy!("private", varType, name, genFunc);
492 }
493 
494 template getterLazy(string writeAccess, varType, string name, string genFunc="")
495 {
496 	enum getterLazy =
497 		"\n"~
498 		((genFunc=="")?
499 			"static if(!is(typeof(_"~name~"_gen)==function))\n"~
500 			`	static assert(false, "'getterLazy!(`~fixAATypeName(varType.stringof)~`, \"`~name~`\")' requires function '`~fixAATypeName(varType.stringof)~` _`~name~`_gen()' to be defined");`~"\n"
501 
502 			// Blocked by DMD Bug #2885
503 			//"static if(!is(ReturnTypeOf!(_"~name~"_gen):"~varType.stringof~"))\n"~
504 			//`	static assert(false, "'getterLazy!(`~varType.stringof~`, \"`~name~`\")' requires function '_`~name~`_gen' to return type '`~varType.stringof~`' (or a compatible type), not type '"~ReturnTypeOf!(_`~name~`_gen).stringof~"'");`~"\n"~
505 
506 			// Forward reference issues prevent this too
507 			//"static if(!ParameterTupleOf!(_line_gen).length==0)\n"~
508 			//`	static assert(false, "'getterLazy!(`~varType.stringof~`, \"`~name~`\")' requires an overload of function '_`~name~`_gen' that takes no arguments");`~"\n"~
509 		:"")~
510 
511 		writeAccess~" "~varType.stringof~" _"~name~";\n"~
512 		writeAccess~" bool _"~name~"_cached = false;\n"~
513 		"@property public "~varType.stringof~" "~name~"() {\n"~
514 		"	if(!_"~name~"_cached) {\n"~
515 		"		_"~name~"_cached = true;\n"~
516 		"		_"~name~" = _"~name~"_gen();\n"~
517 		"	}\n"~
518 		"	return _"~name~";\n"~
519 		"}\n"~
520 		((genFunc=="")?"":
521 			writeAccess~" "~varType.stringof~" _"~name~"_gen()\n"~
522 			"{\n"~
523 			genFunc~
524 			"}\n"
525 		);
526 }
527 
528 /++
529 OBSOLETE IN D2
530 
531 Inserts a compile-time check that ensures a given type is a character type.
532 (ie, char, wchar, or dchar)
533 
534 Usage:
535 
536 ----
537 void funcForStringsOnly(T)(T[] str)
538 {
539 	//Note, the second param is optional
540 	mixin(ensureCharType!("T", "funcForStringsOnly"));
541 	//Do stuff
542 	return str;
543 }
544 funcForStringsOnly("hello"); // Ok
545 funcForStringsOnly([cast(int)1,2,3]); // Compile error
546 ----
547 
548 Turns Into:
549 
550 ----
551 void funcForStringsOnly(T)(T[] str)
552 {
553 	static assert(
554 		is(T==char) || is(T==wchar) || is(T==dchar),
555 		"From 'funcForStringsOnly': 'T' must be char, wchar or dchar, not '"~T.stringof~"'"
556 	);`;
557 	//Do stuff
558 	return str;
559 }
560 funcForStringsOnly("hello"); // Ok
561 funcForStringsOnly([cast(int)1,2,3]); // Compile error
562 ----
563 
564 Compiler Output:
565 
566 ----
567 Error: static assert  "From 'funcForStringsOnly': 'T' must be char, wchar or dchar, not 'int'"
568 ----
569 +/
570 
571 /+template ensureCharType(string nameOfT, string nameOfCaller="")
572 {
573 	enum ensureCharType = 
574 		`static assert(`~"\n"~
575 		`	is(`~nameOfT~`==char) || is(`~nameOfT~`==wchar) || is(`~nameOfT~`==dchar),`~"\n"~
576 		`	"`~(nameOfCaller==""?"":"From '"~nameOfCaller~"': ")~`'`~nameOfT~`' must be char, wchar or dchar, not '"~`~nameOfT~`.stringof~"'"`~"\n"~
577 		`);`;
578 }+/
579 
580 //TODO: Document genEnum
581 public string genEnum(string name, string[] values)
582 {
583 	return
584 		"enum "~name~" {"~values.ctfe_join(", ")~"}\n"~
585 		"enum uint "~name~"_length = "~to!string(values.length)~";\n"~
586 		_genEnumToString(name, values)~
587 		_genStringToEnum(name, values);
588 }
589 
590 // The function this generates could probably be improved.
591 public string _genEnumToString(string enumName, string[] enumValues)
592 {
593 	string ret = "";
594 	
595 	foreach(string enumValue; enumValues)
596 		ret ~= "    if(value=="~enumName~"."~enumValue~") return \""~enumValue~"\";\n";
597 	
598 	ret =
599 		"string enum"~enumName~"ToString("~enumName~" value)\n"~
600 		"{\n"~
601 		ret~
602 		`    throw new Exception("Internal Error: Unhandled value in enum`~enumName~`ToString");`~"\n"~
603 		"}\n";
604 	
605 	return ret;
606 }
607 
608 // The function this generates could probably be improved.
609 public string _genStringToEnum(string enumName, string[] enumValues)
610 {
611 	string ret = "";
612 	
613 	foreach(string enumValue; enumValues)
614 		ret ~= "    if(value==\""~enumValue~"\") return "~enumName~"."~enumValue~";\n";
615 	
616 	ret =
617 		enumName~" stringToEnum"~enumName~"(string value)\n"~
618 		"{\n"~
619 		ret~
620 		`    throw new Exception("'"~value~"' is not a valid value for '`~enumName~`'");`~"\n"~
621 		"}\n";
622 	
623 	return ret;
624 }
625 
626 /++
627 Use verboseSection to display a message, flush it to the screen, and once the
628 scope is over, display how much time the scope took to run. Message and timing
629 info is only displayed when verbosity is enabled. Condition for "Is verbosity
630 enabled?" can be customized with setVerboseSectionCond.
631 
632 Usage:
633 
634 ----
635 bool verbose;
636 
637 void foo()
638 {
639 	mixin(verboseSection!"Running foo");
640 	[...code here...]
641 }
642 
643 void bar()
644 {
645 	{
646 		mixin(verboseSection!"Running bar stage 1");
647 		[...code here...]
648 	}
649 
650 	{
651 		auto msg = "Running bar stage "~to!string(2);
652 		mixin(verboseSection!msg);
653 		[...code here...]
654 	}
655 }
656 
657 void main()
658 {
659 	verbose = false;
660 	writeln("Verbose off");
661 	foo();
662 	bar();
663 
664 	verbose = true;
665 	writeln("Verbose on");
666 	foo();
667 	bar();
668 }
669 ----
670 
671 Output:
672 
673 ----
674 Verbose off
675 Verbose on
676 Running foo...261ms
677 Running bar stage 1...7ms
678 Running bar stage 2...3528ms
679 ----
680 
681 You can change the conditional expression to something other than the default
682 of "verbose" by using setVerboseSectionCond:
683 
684 Usage:
685 
686 ----
687 struct Options
688 {
689 	bool verbose;
690 }
691 Options options;
692 mixin(setVerboseSectionCond!"options.verbose");
693 
694 // Or:
695 
696 int verbosityLevel;
697 mixin(setVerboseSectionCond!"verbosityLevel > 2");
698 ----
699 
700 Using setVerboseSectionCond only affects the current module.
701 Use it again in each module desired.
702 +/
703 template setVerboseSectionCond(string cond="verbose")
704 {
705 	immutable setVerboseSectionCond = `
706 		private template verboseSection(alias msg)
707 		{
708 			enum verboseSection = verboseSectionEx!(`~cond.stringof~`, msg);
709 		}
710 	`;
711 }
712 
713 // The default
714 ///ditto
715 template verboseSection(alias msg)
716 {
717 	enum verboseSection = verboseSectionEx!("verbose", msg);
718 }
719 
720 public import std.datetime : _semitwist_util_mixins_StopWatchType = StopWatch;
721 public import std.stdio :
722 	_semitwist_util_mixins_write = write,
723 	_semitwist_util_mixins_writeln = writeln,
724 	_semitwist_util_mixins_stdout = stdout;
725 
726 template verboseSectionEx(string verboseExpr, alias msg)
727 {
728 	immutable verboseSectionEx = `
729 		_semitwist_util_mixins_StopWatchType _semitwist_util_mixins_stopWatch;
730 		if(`~verboseExpr~`)
731 		{
732 			_semitwist_util_mixins_write(`~msg.stringof~` ~ "...");
733 			_semitwist_util_mixins_stdout.flush();
734 			_semitwist_util_mixins_stopWatch.start();
735 		}
736 		scope(exit) if(`~verboseExpr~`)
737 		{
738 			_semitwist_util_mixins_writeln(_semitwist_util_mixins_stopWatch.peek.msecs, "ms");
739 			_semitwist_util_mixins_stdout.flush();
740 		}
741 	`;
742 }