1 /// 2 module rxd.meta2.traits; 3 4 enum isEnumValue(T...) = T.length == 1 && 5 __traits(compiles, { enum x = T[0]; }); 6 7 /// 8 //enum allConvertible(alias from, alias to) = 9 // allConvertible!(typeof(from), typeof(to)); 10 11 /// 12 template allConvertible(From, To) 13 { 14 import rxd.meta2.type : isType, isAliasTuple; 15 static if (!isType!From && !isAliasTuple!From && 16 !isType!To && !isAliasTuple!To) 17 enum allConvertible = is(From : To); 18 19 else static if (isType!From) 20 enum allConvertible = allConvertible!(From.typeOf, To); 21 22 else static if (isType!To) 23 enum allConvertible = allConvertible!(From, To.typeOf); 24 25 else static if (isAliasTuple!From && isAliasTuple!To) 26 { 27 static if (From.typeTypeSeq.length != To.typeTypeSeq.length) 28 enum allConvertible = false; 29 30 else static if (From.typeTypeSeq.length == 0) 31 enum allConvertible = true; 32 33 else 34 enum allConvertible = 35 allConvertible!( 36 From.init.front.typeOf, 37 To.init.front.typeOf 38 ) && 39 allConvertible!( 40 typeof(From.init.dropOne()), 41 typeof(To.init.dropOne()) 42 ); 43 } 44 45 else 46 static assert (0, "Unsupported types: " ~ From.stringof ~ " -> " ~ 47 To.stringof); 48 49 } 50 51 import std.meta : Alias, AliasSeq; 52 53 /** 54 * Applies the sequence of arguments `args` to the function or template `fun` 55 * either as function or template arguments, at compile-time or at run-time 56 * (if CTFE is not possible). 57 * 58 * `apply` is most likely to be found used as a building block in higher-order 59 * templates and $(LREF AliasSeq) algorithms like $(LREF staticMap) which need 60 * to call or instantiate (as appropriate) functions or templates, passed as 61 * parameters. 62 * 63 * Params: 64 * fun = function or template to call or instantiate 65 * args = template / function arguments to `apply` to `fun` 66 * 67 * Returns: 68 * An alias to `fun(args)` or `fun!(args)` 69 */ 70 template apply(alias func, args...) 71 { 72 static if (__traits(compiles, { alias res = Alias!(func(args)); })) 73 // Evaluate `fun(args)` at compile-time and wrap the result 74 // in an `Alias`, so it can be used without special-casing 75 // in higher-order templates like `staticMap`. 76 alias apply = Alias!(func(args)); 77 78 else static if (is(typeof(func(args)))) 79 // `fun` is a regular callable, but we can't call it at compile-time. 80 // Delay the evaluation of `fun(args)` to run-time by wrapping it in a 81 // `@property` function. 82 @property auto ref apply() { return func(args); } 83 84 else static if (__traits(compiles, { alias res = Alias!(func!args); })) 85 // `fun` is a manifest constant (a.k.a. `enum`) template or a 86 // function template with no run-time parameters which can be evaulated 87 // at compile-time. Wrap the result in an `Alias`, so it can be aliased. 88 alias apply = Alias!(func!args); 89 90 else static if (__traits(compiles, { alias res = func!args; })) 91 // `fun` is template that yields an `AliasSeq`, or something that can't 92 // be wrapped with `Alias`. 93 alias apply = func!args; 94 95 else 96 static assert (0, "Cannot call / instantiate `" ~ 97 __traits(identifier, func) ~ "` with arguments: `" ~ 98 args.stringof ~ "`."); 99 } 100 101 /** 102 * `apply` can call regular functions with arguments known either at 103 * compile-time, or run-time: 104 */ 105 @safe pure nothrow @nogc 106 unittest 107 { 108 int add(int a, int b) { return a + b; } 109 enum int a = 2, b = 3; 110 enum int sum = apply!(add, a, b); 111 static assert(sum == 5); 112 113 int x = 2, y = 5; 114 int z = apply!((a, b) => a + b, x, y); 115 assert(z == 7); 116 117 // TODO: Show higher-order template example 118 119 // When `apply` is used to call functions it returns the result 120 // by reference (when possible): 121 static struct S { int x; } 122 static ref int getX(ref S s) { return s.x; } 123 static int inc(ref int x) { return ++x; } 124 auto s = S(41); 125 int theAnswer = apply!(inc, apply!(getX, s)); 126 assert (theAnswer == 42); 127 assert (s.x == 42); 128 } 129 130 /** 131 * `apply` can also instantiate function templates with compile-time parameters 132 * and alias or call them: 133 */ 134 @safe nothrow @nogc 135 unittest 136 { 137 int mul(int a, int b)() { return a * b; } 138 139 enum product = apply!(mul, 2, 7); 140 static assert (product == 14); 141 142 static int counter = 0; 143 int offset(int x) { return 10 * ++counter + x; } 144 alias offset7 = apply!(offset, 7); 145 146 assert(offset7() == 17); 147 assert(apply!(offset, 7) == 27); 148 assert(offset7() == 37); 149 } 150 151 /// `apply` can instnatiate manifest constant (a.k.a. `enum`) templates: 152 @safe pure nothrow @nogc 153 unittest 154 { 155 enum ulong square(uint x) = x * x; 156 enum result = apply!(square, 3); 157 static assert(result == 9); 158 } 159 160 /** 161 * Two or more `apply` instances can be chained to provide the template and 162 * later the run-time arguments of a function template: 163 */ 164 @safe pure nothrow @nogc 165 unittest 166 { 167 static T[] transform(alias fun, T)(T[] arr) 168 { 169 foreach (ref e; arr) 170 e = fun(e); 171 return arr; 172 } 173 174 alias result = apply!( 175 apply!(transform, x => x * x, int), 176 [2, 3, 4, 5]); 177 178 static assert (result == [4, 9, 16, 25]); 179 } 180 181 /// `apply` can instnatiate alias templates 182 @safe pure nothrow @nogc 183 unittest 184 { 185 alias ArrayOf(T) = T[]; 186 alias ArrayType = apply!(ArrayOf, int); 187 static assert (is(ArrayType == int[])); 188 189 import std.traits : CommonType; 190 alias Types = AliasSeq!(byte, short, int, long); 191 static assert (is(apply!(CommonType, Types) == long)); 192 } 193 194 /// `apply` can instnatiate higher-order templates that yield alias sequences: 195 @safe pure nothrow @nogc 196 unittest 197 { 198 import std.meta : staticMap; 199 200 alias ArrayOf(T) = T[]; 201 alias Types = AliasSeq!(byte, short, int, long); 202 alias ArrayTypes = apply!(staticMap, ArrayOf, Types); 203 static assert (is(ArrayTypes == AliasSeq!(byte[], short[], int[], long[]))); 204 205 template Overloads(T, string member) 206 { 207 alias Overloads = AliasSeq!(__traits(getOverloads, T, member)); 208 } 209 210 struct S 211 { 212 static int use(int x) { return x + 1; } 213 static bool use(char c) { return c >= '0' && c <= '9'; } 214 static char use(string s) { return s[0]; } 215 } 216 217 static assert(apply!(Overloads, S, "use").length == 3); 218 219 alias atIndex(size_t idx, List...) = Alias!(List[idx]); 220 221 static assert( 222 apply!( // 3) call the function with the argument `10` 223 apply!( // 2) get the first element (with type `int use(int x)`) 224 atIndex, 225 0, 226 apply!(Overloads, S, "use") // 1) get the `use` overload set 227 ), 228 10 229 ) == 11); 230 231 static assert( 232 apply!(apply!(atIndex, 1, apply!(Overloads, S, "use")), '3') == true); 233 234 alias useOverloadSet = Alias!(__traits(getOverloads, S, "use"))[0]; 235 alias useOverloadSet = Alias!(__traits(getOverloads, S, "use"))[1]; 236 alias useOverloadSet = Alias!(__traits(getOverloads, S, "use"))[2]; 237 238 static assert(useOverloadSet(41) == 42); 239 //static assert(useOverloadSet('@') == false); 240 //static assert(useOverloadSet('7') == true); 241 static assert(useOverloadSet("Voldemort") == 'V'); 242 243 template AliasTuple(T...) 244 { 245 alias expand = T; 246 } 247 248 template applyN(alias fun, Tuples...) 249 if (Tuples.length > 0) 250 { 251 static if (Tuples.length == 1) 252 alias applyN = apply!(fun, Tuples[0].expand); 253 else 254 alias applyN = applyN!( 255 apply!(fun, Tuples[0].expand), 256 Tuples[1 .. $]); 257 } 258 259 //alias AT = AliasTuple; 260 //applyN!(Overloads, AT!(S, "use"), atIndex, 2, "asd"); 261 //applyN!("use", ApplyLeft!(Overloads, S), ApplyLeft!(atIndex, 2) 262 263 static assert( 264 apply!(apply!(atIndex, 2, apply!(Overloads, S, "use")), "asd") == 'a'); 265 } 266 267 version (unittest) 268 { 269 template staticMap1(alias func, Args...) if (Args.length > 0) 270 { 271 static if (Args.length == 1) 272 alias staticMap1 = func!(Args[0]); 273 else 274 alias staticMap1 = AliasSeq!( 275 func!(Args[0]), 276 staticMap1!(func, Args[1 .. $])); 277 } 278 279 template staticMap2(alias func, Args...) if (Args.length > 0) 280 { 281 static if (Args.length == 1) 282 alias staticMap2 = apply!(func, Args[0]); 283 else 284 alias staticMap2 = AliasSeq!( 285 apply!(func, Args[0]), 286 staticMap2!(func, Args[1 .. $])); 287 } 288 } 289 290 // Undcoumented, because `staticMap1` and `staticMap2` can't be defined inside 291 // the unittest. 292 @safe pure nothrow @nogc 293 unittest 294 { 295 enum square(int x) = x * x; 296 alias numbers = AliasSeq!(1, 2, 3); 297 298 static assert (!__traits(compiles, 299 { alias result = staticMap1!(x => x * x, numbers); })); 300 301 alias result = staticMap2!(x => x * x, numbers); 302 static assert([result] == [1, 4, 9]); 303 } 304 305 //template staticMapReduce(al 306 307 308 template allMatchFirst(alias pred, list...) 309 if (list.length >= 2) 310 { 311 //static if (is(typeof(pred(list[0], list[1])) : bool)) 312 //{ 313 // static if (list.length == 2) 314 // bool allMatchFirst = pred(list[0], list[1]); 315 316 // else {} 317 //bool allMatchFirst = 318 // allPairs( 319 //} 320 //else (is(typeof(pred!(list[0], list[1])): bool)) 321 //{ 322 // } 323 //else 324 // static assert (0, "Can't call/instantiate pred with ", List.stringof); 325 } 326 327 328 template allPairs(alias pred, List...) 329 { 330 alias first = List[0]; 331 alias rest = List[1 .. $]; 332 333 //static if (is(typeof(pred(List[0], List[1])))) 334 335 336 337 } 338 339 alias TypeOf(T) = T; 340 alias TypeOf(alias sym) = typeof(sym); 341 342 template sameFunctionSignature(alias F1, alias F2) 343 { 344 import std.traits : isSomeFunction; 345 346 enum bool sameFunctionSignature = 347 isSomeFunction!F1 && isSomeFunction!F2 && 348 is(TypeOf!F1 == TypeOf!F2); 349 } 350 351 unittest 352 { 353 import std.meta : AliasSeq; 354 void f0(int x, int y); 355 void f1(int x, int y); 356 void f2(short x, short y); 357 void f3(const int x, const int y); 358 void f4(ref int x, ref int y); 359 void f5(scope int x, scope int y); 360 361 alias all = AliasSeq!(f1, f2, f3, f4, f5); 362 363 //static foreach (idx, f1; all[0 .. $ - 1) 364 //static foreach (f2; all[idx .. $]) 365 //static assert (sameFunctionSignature!(f0, f1)); 366 367 } 368 369 alias Func(RT, Args...) = RT function(Args); 370 371 /// Checks if `Model`'s parameter types are convertible to `Func`'s 372 /// parameter types and `Func`'s return type can be converted 373 /// to Model's return type. 374 template isFuncLike(Func, Model) 375 { 376 import std.traits : ReturnType, Parameters, isImplicitlyConvertible; 377 import rxd.meta2.type : AliasTuple, TypeSeq; 378 379 enum isFuncLike = 380 is(ReturnType!Func : ReturnType!Model) && 381 allConvertible!( 382 AliasTuple!(TypeSeq!(Parameters!Model)), 383 AliasTuple!(TypeSeq!(Parameters!Func))); 384 } 385 386 /// 387 unittest 388 { 389 struct S 390 { 391 int f1(int) { return 0; } 392 float f2(int) { return 0; } 393 int f3(float) { return 0; } 394 float f4(float) { return 0; } 395 } 396 397 static assert ( isFuncLike!(typeof(&S.f1), Func!(int, int))); 398 static assert (!isFuncLike!(typeof(&S.f2), Func!(int, int))); 399 static assert ( isFuncLike!(typeof(&S.f3), Func!(int, int))); 400 static assert (!isFuncLike!(typeof(&S.f4), Func!(int, int))); 401 402 static assert ( isFuncLike!(typeof(&S.f1), Func!(float, int))); 403 static assert ( isFuncLike!(typeof(&S.f2), Func!(float, int))); 404 static assert ( isFuncLike!(typeof(&S.f3), Func!(float, int))); 405 static assert ( isFuncLike!(typeof(&S.f4), Func!(float, int))); 406 407 static assert (!isFuncLike!(typeof(&S.f1), Func!(int, float))); 408 static assert (!isFuncLike!(typeof(&S.f2), Func!(int, float))); 409 static assert ( isFuncLike!(typeof(&S.f3), Func!(int, float))); 410 static assert (!isFuncLike!(typeof(&S.f4), Func!(int, float))); 411 412 static assert (!isFuncLike!(typeof(&S.f1), Func!(float, float))); 413 static assert (!isFuncLike!(typeof(&S.f2), Func!(float, float))); 414 static assert ( isFuncLike!(typeof(&S.f3), Func!(float, float))); 415 static assert ( isFuncLike!(typeof(&S.f4), Func!(float, float))); 416 } 417 418 unittest 419 { 420 class Person {} 421 class Employee : Person {} 422 423 Person g1(Person) { return null; } 424 Person g2(Employee) { return null; } 425 Person g3(Object) { return null; } 426 Employee g4(Person) { return null; } 427 Employee g5(Employee) { return null; } 428 Employee g6(Object) { return null; } 429 Object g7(Person) { return null; } 430 Object g8(Employee) { return null; } 431 Object g9(Object) { return null; } 432 433 434 }