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 }