1 module decorated; 2 3 import std.traits : isCallable; 4 5 private enum isDecoratedName(string str) = { 6 import std.ascii : isDigit; 7 import std.uni : isAlpha; 8 9 alias alpha = (in dchar c) => c == '_' || isAlpha(c); 10 alias alphanum = (in dchar c) => alpha(c) || isDigit(c); 11 12 if (!str.length || isDigit(str[0])) return false; 13 14 foreach (dchar dc; str) if (!alphanum(dc)) return false; 15 16 return true; 17 } (); 18 private enum isDecoratedFun(alias smb) = isCallable!smb || __traits(isTemplate, smb); 19 20 struct decor(alias fun, args...) if(isDecoratedFun!fun) {} 21 22 mixin template decorated(string name, alias fun) 23 if (isDecoratedName!name && isDecoratedFun!fun) 24 { 25 auto _internal_helper_(Args...)(Args args) 26 { 27 auto impl(attrs...)() 28 { 29 static if (attrs.length) 30 { 31 alias next = impl!(attrs[1..$]); 32 static if (is(attrs[0] : decor!Args, Args...)) 33 { 34 static if (__traits(isTemplate, Args[0])) 35 { 36 import std.meta : Instantiate; 37 import std.traits : ReturnType; 38 alias call = Instantiate!(Args[0], ReturnType!next); 39 40 static if (Args.length > 1) 41 return call(Args[1..$])(next); 42 else 43 return call(next); 44 } 45 else 46 { 47 static if (Args.length > 1) 48 return Args[0](Args[1..$])(next); 49 else 50 return Args[0](next); 51 } 52 } 53 else return attrs[0](next); 54 } 55 else 56 { 57 // if it's a lambda we must return 'fun' 58 static if (__traits(compiles, { return &fun; })) 59 return &fun; 60 else 61 return fun; 62 } 63 } 64 65 auto call = impl!(__traits(getAttributes, _internal_helper_))(); 66 static if (isCallable!(typeof(call))) 67 { 68 import std.traits : ParameterDefaults; 69 static if (args.length) 70 { 71 import core.lifetime : forward; 72 return call(forward!args, ParameterDefaults!fun[args.length .. $]); 73 } 74 else return call(ParameterDefaults!fun); 75 } 76 else return call; 77 } 78 79 mixin("alias "~name~" = _internal_helper_;"); 80 } 81 82 @safe pure nothrow unittest 83 { 84 auto myMap(F)(F f) 85 { 86 import std.algorithm : map; 87 return f().map!"a + 1"; 88 } 89 90 // https://issues.dlang.org/show_bug.cgi?id=22694 91 struct S { 92 import std.algorithm : filter, sum; 93 94 @sum 95 @filter!"a & 1" 96 @myMap 97 mixin decorated!("foo", 98 { 99 import std.range : iota; 100 import std.array : array; 101 return 10.iota.array; 102 }); 103 } 104 105 with (S.init) 106 { 107 assert(foo() == 25); 108 } 109 } 110 111 @safe pure nothrow unittest 112 { 113 auto hello(F)(F f) 114 { 115 return () { 116 return "Hello "~f(); 117 }; 118 } 119 120 auto say(T)(string text) 121 { 122 return (T t) { 123 return () { 124 return text~" "~t(); 125 }; 126 }; 127 } 128 129 // https://issues.dlang.org/show_bug.cgi?id=22694 130 struct S 131 { 132 @hello 133 mixin decorated!("foo", 134 { 135 import std.stdio : writeln; 136 return "Decorated"; 137 }); 138 139 @decor!(say, "D rox:") 140 @hello 141 mixin decorated!("bar", 142 { 143 return "Decorated"; 144 }); 145 146 auto oldBaz() { return "Decorated"; } 147 @decor!(say, "D rox:") 148 @hello 149 mixin decorated!("baz", oldBaz); 150 } 151 152 with (S.init) 153 { 154 assert(foo() == "Hello Decorated"); 155 assert(bar() == "D rox: Hello Decorated"); 156 assert(baz() == "D rox: Hello Decorated"); 157 } 158 } 159 160 @safe nothrow unittest 161 { 162 auto cache(F)(F f) 163 { 164 import std.functional : memoize; 165 alias mem = memoize!f; 166 return &mem; 167 } 168 169 // https://issues.dlang.org/show_bug.cgi?id=22694 170 struct S 171 { 172 static size_t expensive; // simulates an expensive algorithm 173 174 @cache 175 mixin decorated!("cached", (size_t n = 1) 176 { 177 expensive++; // simulates an expensive algorithm 178 return expensive; 179 }); 180 } 181 182 with (S.init) 183 { 184 // runs expensive stuff 185 assert(cached(46) == 1); 186 assert(cached(45) == 2); 187 188 // skips the expensive stuff 189 assert(cached(46) == 1); 190 assert(cached(45) == 2); 191 192 // can be used with default arguments 193 assert(cached() == 3); 194 } 195 }