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 }