1 // This file is written in D programming language 2 /** 3 * Module holds compile-time associative map with heterogeneous keys and values. 4 * 5 * Copyright: © 2013-2014 Anton Gushcha 6 * License: Subject to the terms of the MIT license, as written in the included LICENSE file. 7 * Authors: NCrashed <ncrashed@gmail.com> 8 */ 9 module daemonize.keymap; 10 11 import std.traits; 12 import std.typetuple; 13 14 /** 15 * Simple expression list wrapper. 16 * 17 * See_Also: Expression list at dlang.org documentation. 18 */ 19 template ExpressionList(T...) 20 { 21 alias ExpressionList = T; 22 } 23 /// Example 24 unittest 25 { 26 static assert([ExpressionList!(1, 2, 3)] == [1, 2, 3]); 27 } 28 29 /** 30 * Sometimes we don't want to auto expand expression ExpressionLists. 31 * That can be used to pass several lists into templates without 32 * breaking their boundaries. 33 */ 34 template StrictExpressionList(T...) 35 { 36 alias expand = T; 37 } 38 /// Example 39 unittest 40 { 41 template Test(alias T1, alias T2) 42 { 43 static assert([T1.expand] == [1, 2]); 44 static assert([T2.expand] == [3, 4]); 45 enum Test = true; 46 } 47 48 static assert(Test!(StrictExpressionList!(1, 2), StrictExpressionList!(3, 4))); 49 } 50 51 /** 52 * Same as std.typetyple.staticMap, but passes two arguments to the first template. 53 */ 54 template staticMap2(alias F, T...) 55 { 56 static assert(T.length % 2 == 0); 57 58 static if (T.length < 2) 59 { 60 alias staticMap2 = ExpressionList!(); 61 } 62 else static if (T.length == 2) 63 { 64 alias staticMap2 = ExpressionList!(F!(T[0], T[1])); 65 } 66 else 67 { 68 alias staticMap2 = ExpressionList!(F!(T[0], T[1]), staticMap2!(F, T[2 .. $])); 69 } 70 } 71 /// Example 72 unittest 73 { 74 template Test(T...) 75 { 76 enum Test = T[0] && T[1]; 77 } 78 79 static assert([staticMap2!(Test, true, true, true, false)] == [true, false]); 80 } 81 82 /** 83 * Performs filtering of expression tuple $(B T) one by one by function or template $(B F). If $(B F) 84 * returns $(B true) the resulted element goes to returned expression tuple, else it is discarded. 85 */ 86 template staticFilter(alias F, T...) 87 { 88 static if(T.length == 0) 89 { 90 alias staticFilter = ExpressionList!(); 91 } 92 else 93 { 94 static if(__traits(compiles, F(T[0]))) enum result = F(T[0]); 95 else enum result = F!(T[0]); 96 97 static if(result) 98 { 99 alias staticFilter = ExpressionList!(T[0], staticFilter!(F, T[1 .. $])); 100 } 101 else 102 { 103 alias staticFilter = ExpressionList!(staticFilter!(F, T[1 .. $])); 104 } 105 } 106 } 107 /// Example 108 unittest 109 { 110 import std.conv; 111 112 bool testFunc(int val) 113 { 114 return val <= 15; 115 } 116 117 static assert(staticFilter!(testFunc, ExpressionList!(42, 108, 15, 2)) == ExpressionList!(15, 2)); 118 } 119 120 /** 121 * Performs filtering of expression tuple $(B T) by pairs by function or template $(B F). If $(B F) 122 * returns $(B true) the resulted pair goes to returned expression tuple, else it is discarded. 123 */ 124 template staticFilter2(alias F, T...) 125 { 126 static assert(T.length % 2 == 0); 127 128 static if (T.length < 2) 129 { 130 alias staticFilter2 = ExpressionList!(); 131 } 132 else 133 { 134 static if(F(T[0], T[1])) 135 { 136 alias staticFilter2 = ExpressionList!(T[0], T[1], staticFilter2!(F, T[2 .. $])); 137 } 138 else 139 { 140 alias staticFilter2 = ExpressionList!(staticFilter2!(F, T[2 .. $])); 141 } 142 } 143 } 144 /// Example 145 unittest 146 { 147 import std.conv; 148 149 bool testFunc(string val1, int val2) 150 { 151 return val1.to!int == val2; 152 } 153 154 static assert(staticFilter2!(testFunc, ExpressionList!("42", 42, "2", 108, "15", 15, "1", 2)) == ExpressionList!("42", 42, "15", 15)); 155 } 156 157 /** 158 * Static version of std.algorithm.reduce (or fold). Expects that $(B F) 159 * takes accumulator as first argument and a value as second argument. 160 * 161 * First value of $(B T) have to be a initial value of accumulator. 162 */ 163 template staticFold(alias F, T...) 164 { 165 static if(T.length == 0) // invalid input 166 { 167 alias staticFold = ExpressionList!(); 168 } 169 else static if(T.length == 1) 170 { 171 static if(is(T[0]) || !__traits(compiles, {enum staticFold = T[0];})) 172 alias staticFold = T[0]; 173 else 174 enum staticFold = T[0]; 175 } 176 else 177 { 178 alias staticFold = staticFold!(F, F!(T[0], T[1]), T[2 .. $]); 179 } 180 } 181 /// Example 182 unittest 183 { 184 template summ(T...) 185 { 186 enum summ = T[0] + T[1]; 187 } 188 189 static assert(staticFold!(summ, 0, 1, 2, 3, 4) == 10); 190 191 template preferString(T...) 192 { 193 static if(is(T[0] == string)) 194 alias preferString = T[0]; 195 else 196 alias preferString = T[1]; 197 } 198 199 static assert(is(staticFold!(preferString, void, int, string, bool) == string)); 200 static assert(is(staticFold!(preferString, void, int, double, bool) == bool)); 201 } 202 203 /** 204 * Compile-time variant of std.range.robin for expression ExpressionLists. 205 * 206 * Template expects $(B StrictExpressionList) list as parameter and returns 207 * new expression list where first element is from first expression ExpressionList, 208 * second element is from second ExpressionList and so on, until one of input ExpressionLists 209 * doesn't end. 210 */ 211 template staticRobin(SF...) 212 { 213 // Calculating minimum length of all ExpressionLists 214 private static template minimum(T...) 215 { 216 enum length = T[1].expand.length; 217 enum minimum = T[0] > length ? length : T[0]; 218 } 219 220 enum minLength = staticFold!(minimum, size_t.max, SF); 221 222 private static template robin(ulong i) 223 { 224 private template takeByIndex(alias T) 225 { 226 static if(is(T.expand[i])) 227 alias takeByIndex = T.expand[i]; 228 else 229 { 230 // hack to avoid compile-time lambdas 231 // see http://forum.dlang.org/thread/lkl0lp$204h$1@digitalmars.com 232 static if(__traits(compiles, {enum takeByIndex = T.expand[i];})) 233 { 234 enum takeByIndex = T.expand[i]; 235 } 236 else 237 { 238 alias takeByIndex = T.expand[i]; 239 } 240 } 241 } 242 243 static if(i >= minLength) 244 { 245 alias robin = ExpressionList!(); 246 } 247 else 248 { 249 alias robin = ExpressionList!(staticMap!(takeByIndex, SF), robin!(i+1)); 250 } 251 } 252 253 alias staticRobin = robin!0; 254 } 255 /// Example 256 unittest 257 { 258 alias test = staticRobin!(StrictExpressionList!(int, int, int), StrictExpressionList!(float, float)); 259 static assert(is(test == ExpressionList!(int, float, int, float))); 260 261 alias test2 = staticRobin!(StrictExpressionList!(1, 2), StrictExpressionList!(3, 4, 5), StrictExpressionList!(6, 7)); 262 static assert([test2]== [1, 3, 6, 2, 4, 7]); 263 } 264 265 /** 266 * Static associative map. 267 * 268 * $(B Pairs) is a list of pairs key-value. 269 */ 270 template KeyValueList(Pairs...) 271 { 272 static assert(Pairs.length % 2 == 0, text("KeyValueList is expecting even count of elements, not ", Pairs.length)); 273 274 /// Number of entries in the map 275 enum length = Pairs.length / 2; 276 277 /** 278 * Getting values by keys. If $(B Keys) is a one key, then 279 * returns unwrapped value, else a ExpressionExpressionList of values. 280 */ 281 template get(Keys...) 282 { 283 static assert(Keys.length > 0, "KeyValueList.get is expecting an argument!"); 284 static if(Keys.length == 1) 285 { 286 static if(is(Keys[0]) || !__traits(compiles, { enum Key = Keys[0]; }) ) { 287 alias Key = Keys[0]; 288 } else { 289 enum Key = Keys[0]; 290 static assert(__traits(compiles, Key == Key), text(typeof(Key).stringof, " must have a opEqual!")); 291 } 292 293 private static template innerFind(T...) 294 { 295 static if(T.length == 0) { 296 alias innerFind = ExpressionList!(); 297 } else 298 { 299 static if(is(Keys[0])) { 300 static if(is(T[0] == Key)) { 301 static if(is(T[1])) { 302 alias innerFind = T[1]; 303 } else { 304 enum innerFind = T[1]; 305 } 306 } else { 307 alias innerFind = innerFind!(T[2 .. $]); 308 } 309 } else 310 { 311 static if(__traits(compiles, T[0].opEqual!(Key))) enum cmpRes = T[0].opEqual!(Key); 312 else enum cmpRes = T[0] == Key; 313 314 static if(cmpRes) { 315 static if(is(T[1])) { 316 alias innerFind = T[1]; 317 } else { 318 // hack to avoid compile-time lambdas 319 // see http://forum.dlang.org/thread/lkl0lp$204h$1@digitalmars.com 320 static if(__traits(compiles, {enum innerFind = T[1];})) 321 { 322 enum innerFind = T[1]; 323 } else 324 { 325 alias innerFind = T[1]; 326 } 327 } 328 } else { 329 alias innerFind = innerFind!(T[2 .. $]); 330 } 331 } 332 } 333 } 334 335 alias get = innerFind!Pairs; 336 } else { 337 alias get = ExpressionList!(get!(Keys[0 .. $/2]), get!(Keys[$/2 .. $])); 338 } 339 } 340 341 /// Returns true if map has a $(B Key) 342 template has(Key...) 343 { 344 static assert(Key.length == 1); 345 enum has = ExpressionList!(get!Key).length > 0; 346 } 347 348 /// Setting values to specific keys (or adding new key-values) 349 template set(KeyValues...) 350 { 351 static assert(KeyValues.length >= 2, "KeyValueList.set is expecting at least one pair!"); 352 static assert(KeyValues.length % 2 == 0, "KeyValuesExpressionList.set is expecting even count of arguments!"); 353 354 template inner(KeyValues...) 355 { 356 static if(KeyValues.length == 2) { 357 static if(is(KeyValues[0])) { 358 alias Key = KeyValues[0]; 359 } else { 360 enum Key = KeyValues[0]; 361 } 362 363 static if(is(KeyValues[1])) { 364 alias Value = KeyValues[1]; 365 } else { 366 enum Value = KeyValues[1]; 367 } 368 369 private template innerFind(T...) 370 { 371 static if(T.length == 0) { 372 alias innerFind = ExpressionList!(Key, Value); 373 } else 374 { 375 static if(is(Key)) { 376 static if(is(T[0] == Key)) { 377 alias innerFind = ExpressionList!(Key, Value, T[2 .. $]); 378 } else { 379 alias innerFind = ExpressionList!(T[0 .. 2], innerFind!(T[2 .. $])); 380 } 381 } else 382 { 383 static if(T[0] == Key) { 384 alias innerFind = ExpressionList!(Key, Value, T[2 .. $]); 385 } else { 386 alias innerFind = ExpressionList!(T[0 .. 2], innerFind!(T[2 .. $])); 387 } 388 } 389 } 390 } 391 392 alias inner = innerFind!Pairs; 393 } else { 394 alias inner = ExpressionList!(inner!(KeyValues[0 .. $/2]), inner!(KeyValues[$/2 .. $])); 395 } 396 } 397 alias set = KeyValueList!(inner!KeyValues); 398 } 399 400 /// Applies $(B F) template for each pair (key-value). 401 template map(alias F) 402 { 403 alias map = KeyValueList!(staticMap2!(F, Pairs)); 404 } 405 406 private static template getKeys(T...) 407 { 408 static if(T.length == 0) { 409 alias getKeys = ExpressionList!(); 410 } else { 411 alias getKeys = ExpressionList!(T[0], getKeys!(T[2 .. $])); 412 } 413 } 414 /// Getting expression list of all keys 415 alias keys = getKeys!Pairs; 416 417 private static template getValues(T...) 418 { 419 static if(T.length == 0) { 420 alias getValues = ExpressionList!(); 421 } else { 422 alias getValues = ExpressionList!(T[1], getValues!(T[2 .. $])); 423 } 424 } 425 /// Getting expression list of all values 426 alias values = getValues!Pairs; 427 428 /** 429 * Filters entries with function or template $(B F), leaving entry only if 430 * $(B F) returning $(B true). 431 */ 432 static template filter(alias F) 433 { 434 alias filter = KeyValueList!(staticFilter2!(F, Pairs)); 435 } 436 437 /** 438 * Filters entries with function or template $(B F) passing only a key from an entry, leaving entry only if 439 * $(B F) returning $(B true). 440 */ 441 static template filterByKey(alias F) 442 { 443 private alias newKeys = staticFilter!(F, keys); 444 private alias newValues = staticMap!(get, newKeys); 445 alias filterByKey = KeyValueList!(staticRobin!(StrictExpressionList!newKeys, StrictExpressionList!newValues)); 446 } 447 } 448 /// 449 unittest 450 { 451 alias map = KeyValueList!("a", 42, "b", 23); 452 static assert(map.get!"a" == 42); 453 static assert(map.get!("a", "b") == ExpressionList!(42, 23)); 454 static assert(map.get!"c".length == 0); 455 456 alias map2 = KeyValueList!(int, float, float, double, double, 42); 457 static assert(is(map2.get!int == float)); 458 static assert(is(map2.get!float == double)); 459 static assert(map2.get!double == 42); 460 461 static assert(map.has!"a"); 462 static assert(map2.has!int); 463 static assert(!map2.has!void); 464 static assert(!map.has!"c"); 465 466 alias map3 = map.set!("c", 4); 467 static assert(map3.get!"c" == 4); 468 alias map4 = map.set!("c", 4, "d", 8); 469 static assert(map4.get!("c", "d") == ExpressionList!(4, 8)); 470 alias map5 = map.set!("a", 4); 471 static assert(map5.get!"a" == 4); 472 473 template inc(string key, int val) 474 { 475 alias inc = ExpressionList!(key, val+1); 476 } 477 478 alias map6 = map.map!inc; 479 static assert(map6.get!"a" == 43); 480 static assert(map6.get!("a", "b") == ExpressionList!(43, 24)); 481 482 static assert(map.keys == ExpressionList!("a", "b")); 483 static assert(map.values == ExpressionList!(42, 23)); 484 }