1 /** 2 * Metaprogramming miscellanea 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.misc; 15 16 import std.algorithm; 17 import std.traits; 18 19 /** 20 * Same as TypeTuple, but meant to be used with values. 21 * 22 * Example: 23 * foreach (char channel; ValueTuple!('r', 'g', 'b')) 24 * { 25 * // the loop is unrolled at compile-time 26 * // "channel" is a compile-time value, and can be used in string mixins 27 * } 28 */ 29 template ValueTuple(T...) 30 { 31 alias T ValueTuple; 32 } 33 34 template RangeTupleImpl(size_t N, R...) 35 { 36 static if (N==R.length) 37 alias R RangeTupleImpl; 38 else 39 alias RangeTupleImpl!(N, ValueTuple!(R, R.length)) RangeTupleImpl; 40 } 41 42 /// Generate a tuple containing integers from 0 to N-1. 43 /// Useful for static loop unrolling. (staticIota) 44 template RangeTuple(size_t N) 45 { 46 alias RangeTupleImpl!(N, ValueTuple!()) RangeTuple; 47 } 48 49 template ArrayToTuple(alias arr, Elements...) 50 { 51 static if (arr.length) 52 alias ArrayToTuple = ArrayToTuple!(arr[1..$], ValueTuple!(Elements, arr[0])); 53 else 54 alias ArrayToTuple = Elements; 55 } 56 57 unittest 58 { 59 alias X = ArrayToTuple!"abc"; 60 static assert(X[0] == 'a' && X[2] == 'c'); 61 static assert([X] == "abc"); 62 } 63 64 /// Return something to foreach over optimally. 65 /// If A is known at compile-time, return a tuple, 66 /// so the foreach is unrolled at compile-time. 67 /// Otherwise, return A for a regular runtime foreach. 68 template CTIterate(alias A) 69 { 70 static if (is(typeof(ArrayToTuple!A))) 71 enum CTIterate = ArrayToTuple!A; 72 else 73 alias CTIterate = A; 74 } 75 76 unittest 77 { 78 foreach (c; CTIterate!"abc") {} 79 string s; 80 foreach (c; CTIterate!s) {} 81 } 82 83 /// Like std.typecons.Tuple, but a template mixin. 84 /// Unlike std.typecons.Tuple, names may not be omitted - but repeating types may be. 85 /// Example: FieldList!(ubyte, "r", "g", "b", ushort, "a"); 86 mixin template FieldList(Fields...) 87 { 88 mixin(GenFieldList!(void, Fields)); 89 } 90 91 template GenFieldList(T, Fields...) 92 { 93 static if (Fields.length == 0) 94 enum GenFieldList = ""; 95 else 96 { 97 static if (is(typeof(Fields[0]) == string)) 98 enum GenFieldList = T.stringof ~ " " ~ Fields[0] ~ ";\n" ~ GenFieldList!(T, Fields[1..$]); 99 else 100 enum GenFieldList = GenFieldList!(Fields[0], Fields[1..$]); 101 } 102 } 103 104 unittest 105 { 106 struct S 107 { 108 mixin FieldList!(ubyte, "r", "g", "b", ushort, "a"); 109 } 110 S s; 111 static assert(is(typeof(s.r) == ubyte)); 112 static assert(is(typeof(s.g) == ubyte)); 113 static assert(is(typeof(s.b) == ubyte)); 114 static assert(is(typeof(s.a) == ushort)); 115 } 116 117 /// Return true if all of T's fields are the same type. 118 @property bool isHomogenous(T)() 119 { 120 foreach (i, f; T.init.tupleof) 121 if (!is(typeof(T.init.tupleof[i]) == typeof(T.init.tupleof[0]))) 122 return false; 123 return true; 124 } 125 126 template isValueOfTypeInTuple(X, T...) 127 { 128 static if (T.length==0) 129 enum bool isValueOfTypeInTuple = false; 130 else 131 static if (T.length==1) 132 enum bool isValueOfTypeInTuple = is(typeof(T[0]) : X); 133 else 134 enum bool isValueOfTypeInTuple = isValueOfTypeInTuple!(X, T[0..$/2]) || isValueOfTypeInTuple!(X, T[$/2..$]); 135 } 136 137 unittest 138 { 139 static assert( isValueOfTypeInTuple!(int, ValueTuple!("a", 42))); 140 static assert(!isValueOfTypeInTuple!(int, ValueTuple!("a", 42.42))); 141 static assert(!isValueOfTypeInTuple!(int, ValueTuple!())); 142 143 static assert(!isValueOfTypeInTuple!(int, "a", int, Object)); 144 static assert( isValueOfTypeInTuple!(int, "a", int, Object, 42)); 145 } 146 147 template findValueOfTypeInTuple(X, T...) 148 { 149 static if (T.length==0) 150 static assert(false, "Can't find value of type " ~ X.stringof ~ " in specified tuple"); 151 else 152 static if (is(typeof(T[0]) : X)) 153 enum findValueOfTypeInTuple = T[0]; 154 else 155 enum findValueOfTypeInTuple = findValueOfTypeInTuple!(X, T[1..$]); 156 } 157 158 unittest 159 { 160 static assert(findValueOfTypeInTuple!(int, ValueTuple!("a", 42))==42); 161 static assert(findValueOfTypeInTuple!(int, "a", int, Object, 42)==42); 162 } 163 164 /// One past the biggest element of the enum T. 165 /// Example: string[EnumLength!E] arr; 166 template EnumLength(T) 167 if (is(T==enum)) 168 { 169 enum EnumLength = cast(T)(cast(size_t)T.max + 1); 170 } 171 172 // ************************************************************************ 173 174 // http://d.puremagic.com/issues/show_bug.cgi?id=7805 175 static template stringofArray(Args...) 176 { 177 static string[] stringofArray() 178 { 179 string[] args; 180 foreach (i, _ ; typeof(Args)) 181 args ~= Args[i].stringof; 182 return args; 183 } 184 } 185 186 /// Returns the index of fun's parameter with the name 187 /// matching "names", or asserts if the parameter is not found. 188 /// "names" can contain multiple names separated by slashes. 189 static size_t findParameter(alias fun, string names)() 190 { 191 foreach (name; names.split("/")) 192 foreach (i, param; ParameterIdentifierTuple!fun) 193 if (param == name) 194 return i; 195 assert(false, "Function " ~ __traits(identifier, fun) ~ " doesn't have a parameter called " ~ name); 196 } 197 198 /// ditto 199 // Workaround for no "static alias" template parameters 200 static size_t findParameter()(string[] searchedNames, string soughtNames, string funName) 201 { 202 foreach (soughtName; soughtNames.split("/")) 203 { 204 auto targetIndex = searchedNames.countUntil(soughtName); 205 if (targetIndex >= 0) 206 return targetIndex; 207 } 208 assert(false, "No argument %s in %s's parameters (%s)".format(soughtNames, funName, searchedNames).idup); 209 } 210 211 // ************************************************************************ 212 213 // Using a compiler with UDA support? 214 enum HAVE_UDA = __traits(compiles, __traits(getAttributes, Object)); 215 216 static if (HAVE_UDA) 217 { 218 template hasAttribute(T, alias D) 219 { 220 enum bool hasAttribute = isValueOfTypeInTuple!(T, __traits(getAttributes, D)); 221 } 222 223 template getAttribute(T, alias D) 224 { 225 enum T getAttribute = findValueOfTypeInTuple!(T, __traits(getAttributes, D)); 226 } 227 } 228 else 229 { 230 template hasAttribute(T, alias D) 231 { 232 enum bool hasAttribute = false; 233 } 234 235 template getAttribute(T, alias D) 236 { 237 static assert(false, "This D compiler has no UDA support."); 238 } 239 } 240 241 // ************************************************************************ 242 243 import std.conv; 244 import std.string; 245 246 string mixGenerateContructorProxies(T)() 247 { 248 string s; 249 static if (__traits(hasMember, T, "__ctor")) 250 foreach (ctor; __traits(getOverloads, T, "__ctor")) 251 { 252 string[] declarationList, usageList; 253 foreach (i, param; ParameterTypeTuple!(typeof(&ctor))) 254 { 255 auto varName = "v" ~ text(i); 256 declarationList ~= param.stringof ~ " " ~ varName; 257 usageList ~= varName; 258 } 259 s ~= "this(" ~ declarationList.join(", ") ~ ") { super(" ~ usageList.join(", ") ~ "); }\n"; 260 } 261 return s; 262 } 263 264 /// Generate constructors that simply call the parent class constructors. 265 /// Based on http://forum.dlang.org/post/i3hpj0$2vc6$1@digitalmars.com 266 mixin template GenerateContructorProxies() 267 { 268 mixin(mixGenerateContructorProxies!(typeof(super))()); 269 } 270 271 unittest 272 { 273 class A 274 { 275 int i, j; 276 this() { } 277 this(int i) { this.i = i; } 278 this(int i, int j ) { this.i = i; this.j = j; } 279 } 280 281 class B : A 282 { 283 mixin GenerateContructorProxies; 284 } 285 286 A a; 287 288 a = new B(); 289 assert(a.i == 0); 290 a = new B(17); 291 assert(a.i == 17); 292 a = new B(17, 42); 293 assert(a.j == 42); 294 } 295 296 // ************************************************************************ 297 298 /// Generate a @property function which creates/returns 299 /// a thread-local singleton of a class with the given arguments. 300 301 @property T singleton(T, args...)() 302 if (is(typeof(new T(args)))) 303 { 304 static T instance; 305 if (!instance) 306 instance = new T(args); 307 return instance; 308 } 309 310 unittest 311 { 312 static class C 313 { 314 static int n = 0; 315 316 this() { n++; } 317 this(int x) { n += x; } 318 319 void fun() {} 320 } 321 322 alias singleton!C c0; 323 c0.fun(); 324 c0.fun(); 325 assert(C.n == 1); 326 327 alias singleton!(C, 5) c1; 328 c1.fun(); 329 c1.fun(); 330 assert(C.n == 6); 331 } 332 333 // ************************************************************************ 334 335 /// Were we built with -debug? 336 debug 337 enum isDebug = true; 338 else 339 enum isDebug = false; 340 341 deprecated alias IsDebug = isDebug; 342 343 // ************************************************************************ 344 345 /// Shorter synonym for std.traits.Identity. 346 /// Can be used to UFCS-chain static methods and nested functions. 347 alias I(alias A) = A; 348 349 /// Get f's ancestor which represents its "this" pointer. 350 /// Skips template and mixin ancestors until it finds a struct or class. 351 template thisOf(alias f) 352 { 353 alias p = Identity!(__traits(parent, f)); 354 static if (is(p == class) || is(p == struct) || is(p == union)) 355 alias thisOf = p; 356 else 357 alias thisOf = thisOf!p; 358 } 359 360 // ************************************************************************ 361 362 /// Unsigned integer type big enough to fit N bits of precision. 363 template UnsignedBitsType(uint bits) 364 { 365 static if (bits <= 8) 366 alias ubyte UnsignedBitsType; 367 else 368 static if (bits <= 16) 369 alias ushort UnsignedBitsType; 370 else 371 static if (bits <= 32) 372 alias uint UnsignedBitsType; 373 else 374 static if (bits <= 64) 375 alias ulong UnsignedBitsType; 376 else 377 static assert(0, "No integer type big enough to fit " ~ bits.stringof ~ " bits"); 378 } 379 380 template SignedBitsType(uint bits) 381 { 382 alias Signed!(UnsignedBitsType!bits) SignedBitsType; 383 } 384 385 /// Evaluates to array of strings with name for each field. 386 @property string[] structFields(T)() 387 if (is(T == struct) || is(T == class)) 388 { 389 import std.string : split; 390 391 string[] fields; 392 foreach (i, f; T.init.tupleof) 393 { 394 string field = T.tupleof[i].stringof; 395 field = field.split(".")[$-1]; 396 fields ~= field; 397 } 398 return fields; 399 }