1 /** 2 * Named method and struct literal arguments 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.meta.args; 15 16 import std.traits; 17 18 // Inspired by 19 // http://forum.dlang.org/post/awjuoemsnmxbfgzhgkgx@forum.dlang.org 20 21 /// Simulates named arguments for function calls. 22 /// Accepts arguments as lambdas (name => value) on the template parameter list, 23 /// and positional arguments on the runtime parameter list (see examples below). 24 template args(alias fun, dgs...) 25 if (is(typeof(fun) == function)) 26 { 27 auto args(PosArgs...)(auto ref PosArgs posArgs) 28 { 29 ParameterTypeTuple!fun args; 30 enum names = ParameterIdentifierTuple!fun; 31 32 foreach (i, ref arg; posArgs) 33 args[i] = posArgs[i]; 34 foreach (i, arg; ParameterDefaults!fun) 35 static if (i >= posArgs.length) 36 args[i] = ParameterDefaults!fun[i]; 37 38 // anything works here, but use a custom type to avoid user errors 39 static struct DummyType {} 40 41 foreach (dg; dgs) 42 { 43 alias fun = dg!DummyType; 44 static if (is(FunctionTypeOf!fun PT == __parameters)) 45 { 46 enum name = __traits(identifier, PT); 47 enum index = argIndex!names(name); 48 static assert(index >= 0, "No such argument: " ~ name); 49 args[index] = fun(DummyType.init); 50 } 51 else 52 static assert(false, "Failed to extract parameter name from " ~ fun.stringof); 53 } 54 return fun(args); 55 } 56 } 57 58 /// 59 unittest 60 { 61 static int fun(int a=1, int b=2, int c=3, int d=4, int e=5) 62 { 63 return a+b+c+d+e; 64 } 65 66 assert(args!(fun) == 15); 67 assert(args!(fun, b=>3) == 16); 68 assert(args!(fun, b=>3, d=>3) == 15); 69 70 static assert(!is(typeof(args!(fun, b=>b)()))); 71 static assert(!is(typeof(args!(fun, x=>42)()))); 72 } 73 74 /// Mixing named and positional arguments 75 unittest 76 { 77 static int fun(int a, int b=2, int c=3, int d=4, int e=5) 78 { 79 return a+b+c+d+e; 80 } 81 82 assert(args!(fun)(1) == 15); 83 assert(args!(fun, b=>3)(1) == 16); 84 } 85 86 /// Simulates named arguments for struct literals. 87 template args(S, dgs...) 88 if (is(S == struct)) 89 { 90 @property S args() 91 { 92 S s; 93 94 // anything works here, but use a custom type to avoid user errors 95 static struct DummyType {} 96 97 foreach (dg; dgs) 98 { 99 alias fun = dg!DummyType; 100 static if (is(FunctionTypeOf!fun PT == __parameters)) 101 { 102 enum name = __traits(identifier, PT); 103 enum index = fieldIndex!S(name); 104 static assert(index >= 0, "No such field: " ~ name); 105 s.tupleof[index] = fun(DummyType.init); 106 } 107 else 108 static assert(false, "Failed to extract parameter name from " ~ fun.stringof); 109 } 110 return s; 111 } 112 } 113 114 unittest 115 { 116 static struct S 117 { 118 int a = 1, b = 2, c = 3, d = 4, e = 5; 119 @property int sum() { return a + b + c + d + e; } 120 } 121 122 assert(args!(S).sum == 15); 123 assert(args!(S, b=>3).sum == 16); 124 assert(args!(S, b=>3, d=>3).sum == 15); 125 126 static assert(!is(typeof(args!(S, b=>b)))); 127 static assert(!is(typeof(args!(S, x=>42)))); 128 } 129 130 unittest 131 { 132 struct S 133 { 134 union { int a, b; } 135 } 136 137 assert(args!(S, a => 1).a == 1); 138 assert(args!(S, b => 1).a == 1); 139 } 140 141 private sizediff_t argIndex(names...)(string name) 142 { 143 foreach (i, argName; names) 144 if (name == argName) 145 return i; 146 return -1; 147 } 148 149 private sizediff_t fieldIndex(S)(string name) 150 { 151 import ae.utils.meta : RangeTuple; 152 153 foreach (i; RangeTuple!(S.tupleof.length)) 154 if (__traits(identifier, S.tupleof[i]) == name) 155 return i; 156 return -1; 157 }