1 /++ 2 Functions that manipulate other functions. 3 This module provides functions for compile time function composition. These 4 functions are helpful when constructing predicates for the algorithms in 5 $(MREF mir, ndslice). 6 $(BOOKTABLE $(H2 Functions), 7 $(TR $(TH Function Name) $(TH Description)) 8 $(TR $(TD $(LREF naryFun)) 9 $(TD Create a unary, binary or N-nary function from a string. Most often 10 used when defining algorithms on ranges and slices. 11 )) 12 $(TR $(TD $(LREF pipe)) 13 $(TD Join a couple of functions into one that executes the original 14 functions one after the other, using one function's result for the next 15 function's argument. 16 )) 17 $(TR $(TD $(LREF not)) 18 $(TD Creates a function that negates another. 19 )) 20 $(TR $(TD $(LREF reverseArgs)) 21 $(TD Predicate that reverses the order of its arguments. 22 )) 23 $(TR $(TD $(LREF forward)) 24 $(TD Forwards function arguments with saving ref-ness. 25 )) 26 $(TR $(TD $(LREF tuple)) 27 $(TD Removes $(LREF Ref) shell. 28 )) 29 $(TR $(TD $(LREF unref)) 30 $(TD Creates a $(LREF Tuple) structure. 31 )) 32 $(TR $(TD $(LREF __ref)) 33 $(TD Creates a $(LREF Ref) structure. 34 )) 35 ) 36 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 37 Authors: Ilia Ki, $(HTTP erdani.org, Andrei Alexandrescu (some original code from std.functional)) 38 39 Macros: 40 NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 41 +/ 42 module mir.functional; 43 44 private enum isRef(T) = is(T : Ref!T0, T0); 45 46 import mir.math.common: optmath; 47 48 public import core.lifetime : forward; 49 50 @optmath: 51 52 /++ 53 Constructs static array. 54 +/ 55 T[N] staticArray(T, size_t N)(return scope T[N] a...) { 56 import std.traits: isDynamicArray; 57 static if (isDynamicArray!T) { 58 T[N] ret; 59 static foreach(i; 0..a.length) ret[i] = a[i]; 60 return ret; 61 } 62 else return a; 63 } 64 65 @safe version(mir_core_test) unittest 66 { 67 string[2] v = ["AA", "BB"]; 68 auto res = staticArray(v); 69 assert(res == v); 70 } 71 72 /++ 73 Simple wrapper that holds a pointer. 74 It is used for as workaround to return multiple auto ref values. 75 +/ 76 struct Ref(T) 77 if (!isRef!T) 78 { 79 @optmath: 80 81 @disable this(); 82 /// 83 this(ref T value) @trusted 84 { 85 __ptr = &value; 86 } 87 /// 88 T* __ptr; 89 /// 90 ref inout(T) __value() inout @property { return *__ptr; } 91 /// 92 alias __value this; 93 94 /// 95 bool opEquals(ref scope const T rhs) const scope 96 { 97 return __value == rhs; 98 } 99 100 /// 101 bool opEquals(scope const T rhs) const scope 102 { 103 return __value == rhs; 104 } 105 106 static if (__traits(hasMember, T, "toHash") || __traits(isScalar, T)) 107 /// 108 size_t toHash() const 109 { 110 return hashOf(__value); 111 } 112 } 113 114 /// Creates $(LREF Ref) wrapper. 115 Ref!T _ref(T)(ref T value) 116 { 117 return Ref!T(value); 118 } 119 120 private mixin template _RefTupleMixin(T...) 121 if (T.length <= 26) 122 { 123 static if (T.length) 124 { 125 enum i = T.length - 1; 126 static if (isRef!(T[i])) 127 mixin(`@optmath @property ref ` ~ cast(char)('a' + i) ~ `() { return *expand[` ~ i.stringof ~ `].__ptr; }` ); 128 else 129 mixin(`alias ` ~ cast(char)('a' + i) ~ ` = expand[` ~ i.stringof ~ `];`); 130 mixin ._RefTupleMixin!(T[0 .. $-1]); 131 } 132 } 133 134 /++ 135 Simplified tuple structure. Some fields may be type of $(LREF Ref). 136 Ref stores a pointer to a values. 137 +/ 138 struct Tuple(T...) 139 { 140 @optmath: 141 T expand; 142 alias expand this; 143 mixin _RefTupleMixin!T; 144 } 145 146 deprecated("Use 'Tuple' instead") 147 alias RefTuple = Tuple; 148 149 /// Removes $(LREF Ref) shell. 150 alias Unref(V : Ref!T, T) = T; 151 /// ditto 152 template Unref(V : Tuple!T, T...) 153 { 154 import std.meta: staticMap; 155 alias Unref = Tuple!(staticMap!(.Unref, T)); 156 } 157 158 /// ditto 159 alias Unref(V) = V; 160 161 /++ 162 Returns: a $(LREF Tuple) structure. 163 +/ 164 Tuple!Args tuple(Args...)(auto ref Args args) 165 { 166 return Tuple!Args(args); 167 } 168 169 deprecated("Use 'tuple' instead") 170 alias refTuple = tuple; 171 172 /// Removes $(LREF Ref) shell. 173 ref T unref(V : Ref!T, T)(scope return V value) 174 { 175 return *value.__ptr; 176 } 177 178 /// ditto 179 Unref!(Tuple!T) unref(V : Tuple!T, T...)(V value) 180 { 181 typeof(return) ret; 182 foreach(i, ref elem; ret.expand) 183 elem = unref(value.expand[i]); 184 return ret; 185 } 186 187 /// ditto 188 ref V unref(V)(scope return ref V value) 189 { 190 return value; 191 } 192 193 /// ditto 194 V unref(V)(V value) 195 { 196 import std.traits: hasElaborateAssign; 197 static if (hasElaborateAssign!V) 198 { 199 import core.lifetime: move; 200 return move(value); 201 } 202 else 203 return value; 204 } 205 206 private template autoExpandAndForwardElem(alias value) 207 { 208 209 } 210 211 template autoExpandAndForward(alias value) 212 if (is(typeof(value) : Tuple!Types, Types...)) 213 { 214 215 import core.lifetime: move; 216 enum isLazy = __traits(isRef, value) || __traits(isOut, value) || __traits(isLazy, value); 217 template autoExpandAndForwardElem(size_t i) 218 { 219 alias T = typeof(value.expand[i]); 220 static if (isRef!T) 221 { 222 ref autoExpandAndForwardElem() 223 { 224 return *value.expand[i].__ptr; 225 } 226 } 227 else 228 { 229 static if (isLazy) 230 @property ref autoExpandAndForwardElem(){ pragma(inline, true); return value.expand[i]; } 231 else 232 static if (is(typeof(move(value.expand[i])))) 233 @property auto autoExpandAndForwardElem(){ pragma(inline, true); return move(value.expand[i]); } 234 else 235 @property auto autoExpandAndForwardElem(){ pragma(inline, true); return value.expand[i]; } 236 } 237 } 238 239 import mir.internal.utility: Iota; 240 import std.meta: staticMap; 241 alias autoExpandAndForward = staticMap!(autoExpandAndForwardElem, Iota!(value.expand.length)); 242 } 243 244 version(mir_core_test) unittest 245 { 246 long v; 247 auto tup = tuple(v._ref, 2.3); 248 249 auto f(ref long a, double b) 250 { 251 assert(b == 2.3); 252 assert(a == v); 253 assert(&a == &v); 254 } 255 256 f(autoExpandAndForward!tup); 257 } 258 259 private string joinStrings()(string[] strs) 260 { 261 if (strs.length) 262 { 263 auto ret = strs[0]; 264 foreach(s; strs[1 .. $]) 265 ret ~= s; 266 return ret; 267 } 268 return null; 269 } 270 271 private auto copyArg(alias a)() 272 { 273 return a; 274 } 275 276 /++ 277 Takes multiple functions and adjoins them together. The result is a 278 $(LREF Tuple) with one element per passed-in function. Upon 279 invocation, the returned tuple is the adjoined results of all 280 functions. 281 Note: In the special case where only a single function is provided 282 (`F.length == 1`), adjoin simply aliases to the single passed function 283 (`F[0]`). 284 +/ 285 template adjoin(fun...) if (fun.length && fun.length <= 26) 286 { 287 static if (fun.length != 1) 288 { 289 import std.meta: staticMap, Filter; 290 static if (Filter!(_needNary, fun).length == 0) 291 { 292 /// 293 @optmath auto adjoin(Args...)(auto ref Args args) 294 { 295 template _adjoin(size_t i) 296 { 297 static if (__traits(compiles, &(fun[i](forward!args)))) 298 enum _adjoin = "Ref!(typeof(fun[" ~ i.stringof ~ "](forward!args)))(fun[" ~ i.stringof ~ "](args)), "; 299 else 300 enum _adjoin = "fun[" ~ i.stringof ~ "](args), "; 301 } 302 303 import mir.internal.utility; 304 mixin("return tuple(" ~ [staticMap!(_adjoin, Iota!(fun.length))].joinStrings ~ ");"); 305 } 306 } 307 else alias adjoin = .adjoin!(staticMap!(naryFun, fun)); 308 } 309 else alias adjoin = naryFun!(fun[0]); 310 } 311 312 /// 313 @safe version(mir_core_test) unittest 314 { 315 static bool f1(int a) { return a != 0; } 316 static int f2(int a) { return a / 2; } 317 auto x = adjoin!(f1, f2)(5); 318 assert(is(typeof(x) == Tuple!(bool, int))); 319 assert(x.a == true && x.b == 2); 320 } 321 322 @safe version(mir_core_test) unittest 323 { 324 alias f = pipe!(adjoin!("a", "a * a"), "a[0]"); 325 static assert(is(typeof(f(3)) == int)); 326 auto d = 4; 327 static assert(is(typeof(f(d)) == Ref!int)); 328 } 329 330 @safe version(mir_core_test) unittest 331 { 332 static bool F1(int a) { return a != 0; } 333 auto x1 = adjoin!(F1)(5); 334 static int F2(int a) { return a / 2; } 335 auto x2 = adjoin!(F1, F2)(5); 336 assert(is(typeof(x2) == Tuple!(bool, int))); 337 assert(x2.a && x2.b == 2); 338 auto x3 = adjoin!(F1, F2, F2)(5); 339 assert(is(typeof(x3) == Tuple!(bool, int, int))); 340 assert(x3.a && x3.b == 2 && x3.c == 2); 341 342 bool F4(int a) { return a != x1; } 343 alias eff4 = adjoin!(F4); 344 static struct S 345 { 346 bool delegate(int) @safe store; 347 int fun() { return 42 + store(5); } 348 } 349 S s; 350 s.store = (int a) { return eff4(a); }; 351 auto x4 = s.fun(); 352 assert(x4 == 43); 353 } 354 355 //@safe 356 version(mir_core_test) unittest 357 { 358 import std.meta: staticMap; 359 alias funs = staticMap!(naryFun, "a", "a * 2", "a * 3", "a * a", "-a"); 360 alias afun = adjoin!funs; 361 int a = 5, b = 5; 362 assert(afun(a) == tuple(Ref!int(a), 10, 15, 25, -5)); 363 assert(afun(a) == tuple(Ref!int(b), 10, 15, 25, -5)); 364 365 static class C{} 366 alias IC = immutable(C); 367 IC foo(){return typeof(return).init;} 368 Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)(); 369 370 static struct S{int* p;} 371 alias IS = immutable(S); 372 IS bar(){return typeof(return).init;} 373 enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)(); 374 } 375 376 private template needOpCallAlias(alias fun) 377 { 378 /* Determine whether or not naryFun need to alias to fun or 379 * fun.opCall. Basically, fun is a function object if fun(...) compiles. We 380 * want is(naryFun!fun) (resp., is(naryFun!fun)) to be true if fun is 381 * any function object. There are 4 possible cases: 382 * 383 * 1) fun is the type of a function object with static opCall; 384 * 2) fun is an instance of a function object with static opCall; 385 * 3) fun is the type of a function object with non-static opCall; 386 * 4) fun is an instance of a function object with non-static opCall. 387 * 388 * In case (1), is(naryFun!fun) should compile, but does not if naryFun 389 * aliases itself to fun, because typeof(fun) is an error when fun itself 390 * is a type. So it must be aliased to fun.opCall instead. All other cases 391 * should be aliased to fun directly. 392 */ 393 static if (is(typeof(fun.opCall) == function)) 394 { 395 import std.traits: Parameters; 396 enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () { 397 return fun(Parameters!fun.init); 398 }); 399 } 400 else 401 enum needOpCallAlias = false; 402 } 403 404 private template _naryAliases(size_t n) 405 if (n <= 26) 406 { 407 static if (n == 0) 408 enum _naryAliases = ""; 409 else 410 { 411 enum i = n - 1; 412 enum _naryAliases = _naryAliases!i ~ "alias " ~ cast(char)('a' + i) ~ " = args[" ~ i.stringof ~ "];\n"; 413 } 414 } 415 416 private template stringFun(string fun) 417 { 418 /// Specialization for string lambdas 419 @optmath auto ref stringFun(Args...)(auto ref Args args) 420 if (args.length <= 26 && (Args.length == 0) == (fun.length == 0)) 421 { 422 import mir.math.common; 423 static if (fun.length) 424 { 425 mixin(_naryAliases!(Args.length)); 426 return mixin(fun); 427 } 428 else 429 { 430 return; 431 } 432 } 433 } 434 435 /++ 436 Aliases itself to a set of functions. 437 438 Transforms strings representing an expression into a binary function. The 439 strings must use symbol names `a`, `b`, ..., `z` as the parameters. 440 If `functions[i]` is not a string, `naryFun` aliases itself away to `functions[i]`. 441 +/ 442 template naryFun(functions...) 443 if (functions.length >= 1) 444 { 445 static foreach (fun; functions) 446 { 447 static if (is(typeof(fun) : string)) 448 { 449 alias naryFun = stringFun!fun; 450 } 451 else static if (needOpCallAlias!fun) 452 alias naryFun = fun.opCall; 453 else 454 alias naryFun = fun; 455 } 456 } 457 458 /// 459 @safe version(mir_core_test) unittest 460 { 461 // Strings are compiled into functions: 462 alias isEven = naryFun!("(a & 1) == 0"); 463 assert(isEven(2) && !isEven(1)); 464 } 465 466 /// 467 @safe version(mir_core_test) unittest 468 { 469 alias less = naryFun!("a < b"); 470 assert(less(1, 2) && !less(2, 1)); 471 alias greater = naryFun!("a > b"); 472 assert(!greater("1", "2") && greater("2", "1")); 473 } 474 475 /// `naryFun` accepts up to 26 arguments. 476 @safe version(mir_core_test) unittest 477 { 478 assert(naryFun!("a * b + c")(2, 3, 4) == 10); 479 } 480 481 /// `naryFun` can return by reference. 482 version(mir_core_test) unittest 483 { 484 int a; 485 assert(&naryFun!("a")(a) == &a); 486 } 487 488 /// `args` parameter tuple 489 version(mir_core_test) unittest 490 { 491 assert(naryFun!("args[0] + args[1]")(2, 3) == 5); 492 } 493 494 /// Multiple functions 495 @safe pure nothrow @nogc 496 version(mir_core_test) unittest 497 { 498 alias fun = naryFun!( 499 (uint a) => a, 500 (ulong a) => a * 2, 501 a => a * 3, 502 ); 503 504 int a = 10; 505 long b = 10; 506 float c = 10; 507 508 assert(fun(a) == 10); 509 assert(fun(b) == 20); 510 assert(fun(c) == 30); 511 } 512 513 @safe version(mir_core_test) unittest 514 { 515 static int f1(int a) { return a + 1; } 516 static assert(is(typeof(naryFun!(f1)(1)) == int)); 517 assert(naryFun!(f1)(41) == 42); 518 int f2(int a) { return a + 1; } 519 static assert(is(typeof(naryFun!(f2)(1)) == int)); 520 assert(naryFun!(f2)(41) == 42); 521 assert(naryFun!("a + 1")(41) == 42); 522 523 int num = 41; 524 assert(naryFun!"a + 1"(num) == 42); 525 526 // Issue 9906 527 struct Seen 528 { 529 static bool opCall(int n) { return true; } 530 } 531 static assert(needOpCallAlias!Seen); 532 static assert(is(typeof(naryFun!Seen(1)))); 533 assert(naryFun!Seen(1)); 534 535 Seen s; 536 static assert(!needOpCallAlias!s); 537 static assert(is(typeof(naryFun!s(1)))); 538 assert(naryFun!s(1)); 539 540 struct FuncObj 541 { 542 bool opCall(int n) { return true; } 543 } 544 FuncObj fo; 545 static assert(!needOpCallAlias!fo); 546 static assert(is(typeof(naryFun!fo))); 547 assert(naryFun!fo(1)); 548 549 // Function object with non-static opCall can only be called with an 550 // instance, not with merely the type. 551 static assert(!is(typeof(naryFun!FuncObj))); 552 } 553 554 @safe version(mir_core_test) unittest 555 { 556 static int f1(int a, string b) { return a + 1; } 557 static assert(is(typeof(naryFun!(f1)(1, "2")) == int)); 558 assert(naryFun!(f1)(41, "a") == 42); 559 string f2(int a, string b) { return b ~ "2"; } 560 static assert(is(typeof(naryFun!(f2)(1, "1")) == string)); 561 assert(naryFun!(f2)(1, "4") == "42"); 562 assert(naryFun!("a + b")(41, 1) == 42); 563 //@@BUG 564 //assert(naryFun!("return a + b;")(41, 1) == 42); 565 566 // Issue 9906 567 struct Seen 568 { 569 static bool opCall(int x, int y) { return true; } 570 } 571 static assert(is(typeof(naryFun!Seen))); 572 assert(naryFun!Seen(1,1)); 573 574 struct FuncObj 575 { 576 bool opCall(int x, int y) { return true; } 577 } 578 FuncObj fo; 579 static assert(!needOpCallAlias!fo); 580 static assert(is(typeof(naryFun!fo))); 581 assert(naryFun!fo(1,1)); 582 583 // Function object with non-static opCall can only be called with an 584 // instance, not with merely the type. 585 static assert(!is(typeof(naryFun!FuncObj))); 586 } 587 588 589 /++ 590 N-ary predicate that reverses the order of arguments, e.g., given 591 `pred(a, b, c)`, returns `pred(c, b, a)`. 592 +/ 593 template reverseArgs(alias fun) 594 { 595 import std.meta: Reverse; 596 /// 597 @optmath auto ref reverseArgs(Args...)(auto ref Args args) 598 if (is(typeof(fun(Reverse!args)))) 599 { 600 return fun(Reverse!args); 601 } 602 603 } 604 605 /// 606 @safe version(mir_core_test) unittest 607 { 608 int abc(int a, int b, int c) { return a * b + c; } 609 alias cba = reverseArgs!abc; 610 assert(abc(91, 17, 32) == cba(32, 17, 91)); 611 } 612 613 @safe version(mir_core_test) unittest 614 { 615 int a(int a) { return a * 2; } 616 alias _a = reverseArgs!a; 617 assert(a(2) == _a(2)); 618 } 619 620 @safe version(mir_core_test) unittest 621 { 622 int b() { return 4; } 623 alias _b = reverseArgs!b; 624 assert(b() == _b()); 625 } 626 627 @safe version(mir_core_test) unittest 628 { 629 alias gt = reverseArgs!(naryFun!("a < b")); 630 assert(gt(2, 1) && !gt(1, 1)); 631 int x = 42; 632 bool xyz(int a, int b) { return a * x < b / x; } 633 auto foo = &xyz; 634 foo(4, 5); 635 alias zyx = reverseArgs!(foo); 636 assert(zyx(5, 4) == foo(4, 5)); 637 } 638 639 /++ 640 Negates predicate `pred`. 641 +/ 642 template not(alias pred) 643 { 644 static if (!is(typeof(pred) : string) && !needOpCallAlias!pred) 645 /// 646 @optmath bool not(T...)(auto ref T args) 647 { 648 return !pred(args); 649 } 650 else 651 alias not = .not!(naryFun!pred); 652 } 653 654 /// 655 @safe version(mir_core_test) unittest 656 { 657 import std.algorithm.searching : find; 658 import std.uni : isWhite; 659 string a = " Hello, world!"; 660 assert(find!(not!isWhite)(a) == "Hello, world!"); 661 } 662 663 @safe version(mir_core_test) unittest 664 { 665 assert(not!"a != 5"(5)); 666 assert(not!"a != b"(5, 5)); 667 668 assert(not!(() => false)()); 669 assert(not!(a => a != 5)(5)); 670 assert(not!((a, b) => a != b)(5, 5)); 671 assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5)); 672 } 673 674 private template _pipe(size_t n) 675 { 676 static if (n) 677 { 678 enum i = n - 1; 679 enum _pipe = "f[" ~ i.stringof ~ "](" ~ ._pipe!i ~ ")"; 680 } 681 else 682 enum _pipe = "forward!args"; 683 } 684 685 private template _unpipe(alias fun) 686 { 687 import std.traits: TemplateArgsOf, TemplateOf; 688 static if (__traits(compiles, TemplateOf!fun)) 689 static if (__traits(isSame, TemplateOf!fun, .pipe)) 690 alias _unpipe = TemplateArgsOf!fun; 691 else 692 alias _unpipe = fun; 693 else 694 alias _unpipe = fun; 695 696 } 697 698 private enum _needNary(alias fun) = is(typeof(fun) : string) || needOpCallAlias!fun; 699 700 /++ 701 Composes passed-in functions `fun[0], fun[1], ...` returning a 702 function `f(x)` that in turn returns 703 `...(fun[1](fun[0](x)))...`. Each function can be a regular 704 functions, a delegate, a lambda, or a string. 705 +/ 706 template pipe(fun...) 707 { 708 static if (fun.length != 1) 709 { 710 import std.meta: staticMap, Filter; 711 alias f = staticMap!(_unpipe, fun); 712 static if (f.length == fun.length && Filter!(_needNary, f).length == 0) 713 { 714 /// 715 @optmath auto ref pipe(Args...)(auto ref Args args) 716 { 717 return mixin (_pipe!(fun.length)); 718 } 719 } 720 else alias pipe = .pipe!(staticMap!(naryFun, f)); 721 } 722 else alias pipe = naryFun!(fun[0]); 723 } 724 725 /// 726 @safe version(mir_core_test) unittest 727 { 728 assert(pipe!("a + b", a => a * 10)(2, 3) == 50); 729 } 730 731 /// `pipe` can return by reference. 732 version(mir_core_test) unittest 733 { 734 int a; 735 assert(&pipe!("a", "a")(a) == &a); 736 } 737 738 /// Template bloat reduction 739 version(mir_core_test) unittest 740 { 741 enum a = "a * 2"; 742 alias b = e => e + 2; 743 744 alias p0 = pipe!(pipe!(a, b), pipe!(b, a)); 745 alias p1 = pipe!(a, b, b, a); 746 747 static assert(__traits(isSame, p0, p1)); 748 } 749 750 @safe version(mir_core_test) unittest 751 { 752 import std.algorithm.comparison : equal; 753 import std.algorithm.iteration : map; 754 import std.array : split; 755 import std.conv : to; 756 757 // First split a string in whitespace-separated tokens and then 758 // convert each token into an integer 759 assert(pipe!(split, map!(to!(int)))("1 2 3").equal([1, 2, 3])); 760 } 761 762 763 struct AliasCall(T, string methodName, TemplateArgs...) 764 { 765 T __this; 766 alias __this this; 767 768 /// 769 auto lightConst()() const @property 770 { 771 import mir.qualifier; 772 return AliasCall!(LightConstOf!T, methodName, TemplateArgs)(__this.lightConst); 773 } 774 775 /// 776 auto lightImmutable()() immutable @property 777 { 778 import mir.qualifier; 779 return AliasCall!(LightImmutableOf!T, methodName, TemplateArgs)(__this.lightImmutable); 780 } 781 782 this()(auto ref T value) 783 { 784 __this = value; 785 } 786 auto ref opCall(Args...)(auto ref Args args) 787 { 788 import std.traits: TemplateArgsOf; 789 mixin("return __this." ~ methodName ~ (TemplateArgs.length ? "!TemplateArgs" : "") ~ "(forward!args);"); 790 } 791 } 792 793 /++ 794 Replaces call operator (`opCall`) for the value using its method. 795 The funciton is designed to use with $(NDSLICE, topology, vmap) or $(NDSLICE, topology, map). 796 Params: 797 methodName = name of the methods to use for opCall and opIndex 798 TemplateArgs = template arguments 799 +/ 800 template aliasCall(string methodName, TemplateArgs...) 801 { 802 /++ 803 Params: 804 value = the value to wrap 805 Returns: 806 wrapped value with implemented opCall and opIndex methods 807 +/ 808 AliasCall!(T, methodName, TemplateArgs) aliasCall(T)(T value) @property 809 { 810 return typeof(return)(value); 811 } 812 813 /// ditto 814 ref AliasCall!(T, methodName, TemplateArgs) aliasCall(T)(return ref T value) @property @trusted 815 { 816 return *cast(typeof(return)*) &value; 817 } 818 } 819 820 /// 821 @safe pure nothrow version(mir_core_test) unittest 822 { 823 static struct S 824 { 825 auto lightConst()() const @property { return S(); } 826 827 auto fun(size_t ct_param = 1)(size_t rt_param) const 828 { 829 return rt_param + ct_param; 830 } 831 } 832 833 S s; 834 835 auto sfun = aliasCall!"fun"(s); 836 assert(sfun(3) == 4); 837 838 auto sfun10 = aliasCall!("fun", 10)(s); // uses fun!10 839 assert(sfun10(3) == 13); 840 } 841 842 /++ 843 +/ 844 template recurseTemplatePipe(alias Template, size_t N, Args...) 845 { 846 static if (N == 0) 847 alias recurseTemplatePipe = Args; 848 else 849 { 850 alias recurseTemplatePipe = Template!(.recurseTemplatePipe!(Template, N - 1, Args)); 851 } 852 } 853 854 /// 855 @safe version(mir_core_test) unittest 856 { 857 // import mir.ndslice.topology: map; 858 alias map(alias fun) = a => a; // some template 859 static assert (__traits(isSame, recurseTemplatePipe!(map, 2, "a * 2"), map!(map!"a * 2"))); 860 } 861 862 /++ 863 +/ 864 template selfAndRecurseTemplatePipe(alias Template, size_t N, Args...) 865 { 866 static if (N == 0) 867 alias selfAndRecurseTemplatePipe = Args; 868 else 869 { 870 alias selfAndRecurseTemplatePipe = Template!(.selfAndRecurseTemplatePipe!(Template, N - 1, Args)); 871 } 872 } 873 874 /// 875 @safe version(mir_core_test) unittest 876 { 877 // import mir.ndslice.topology: map; 878 alias map(alias fun) = a => a; // some template 879 static assert (__traits(isSame, selfAndRecurseTemplatePipe!(map, 2, "a * 2"), map!(pipe!("a * 2", map!"a * 2")))); 880 } 881 882 /++ 883 +/ 884 template selfTemplatePipe(alias Template, size_t N, Args...) 885 { 886 static if (N == 0) 887 alias selfTemplatePipe = Args; 888 else 889 { 890 alias selfTemplatePipe = Template!(.selfTemplatePipe!(Template, N - 1, Args)); 891 } 892 } 893 894 /// 895 @safe version(mir_core_test) unittest 896 { 897 // import mir.ndslice.topology: map; 898 alias map(alias fun) = a => a; // some template 899 static assert (__traits(isSame, selfTemplatePipe!(map, 2, "a * 2"), map!(pipe!("a * 2", map!"a * 2")))); 900 }