1 /** 2 * Copyright: © 2014-2015 Anton Gushcha 3 * License: Subject to the terms of the Boost 1.0 license as specified in LICENSE file 4 * Authors: Anton Gushcha <ncrashed@gmail.com> 5 * 6 * Template for compile time key-value list - extension of std.traits; 7 */ 8 module stribog.meta.keyvalue; 9 10 import stribog.meta.base; 11 import stribog.meta.map; 12 13 /** 14 * Static associative map. 15 * 16 * $(B Pairs) is a list of pairs key-value. 17 */ 18 template KeyValueList(Pairs...) 19 { 20 static assert(Pairs.length % 2 == 0, text("KeyValueList is expecting even count of elements, not ", Pairs.length)); 21 22 /// Number of entries in the map 23 enum length = Pairs.length / 2; 24 25 /** 26 * Getting values by keys. If $(B Keys) is a one key, then 27 * returns unwrapped value, else a ExpressionExpressionList of values. 28 */ 29 template get(Keys...) 30 { 31 static assert(Keys.length > 0, "KeyValueList.get is expecting an argument!"); 32 static if(Keys.length == 1) 33 { 34 static if(is(Keys[0])) { 35 alias Key = Keys[0]; 36 } else { 37 enum Key = Keys[0]; 38 static assert(__traits(compiles, Key == Key), text(typeof(Key).stringof, " must have a opCmp!")); 39 } 40 41 private static template innerFind(T...) 42 { 43 static if(T.length == 0) { 44 alias innerFind = ExpressionList!(); 45 } else 46 { 47 static if(is(Keys[0])) { 48 static if(is(T[0] == Key)) { 49 static if(is(T[1])) { 50 alias innerFind = T[1]; 51 } else { 52 enum innerFind = T[1]; 53 } 54 } else { 55 alias innerFind = innerFind!(T[2 .. $]); 56 } 57 } else 58 { 59 static if(T[0] == Key) { 60 static if(is(T[1])) { 61 alias innerFind = T[1]; 62 } else { 63 // hack to avoid compile-time lambdas 64 // see http://forum.dlang.org/thread/lkl0lp$204h$1@digitalmars.com 65 static if(__traits(compiles, {enum innerFind = T[1];})) 66 { 67 enum innerFind = T[1]; 68 } else 69 { 70 alias innerFind = T[1]; 71 } 72 } 73 } else { 74 alias innerFind = innerFind!(T[2 .. $]); 75 } 76 } 77 } 78 } 79 80 alias get = innerFind!Pairs; 81 } else { 82 alias get = ExpressionList!(get!(Keys[0 .. $/2]), get!(Keys[$/2 .. $])); 83 } 84 } 85 86 /// Returns true if map has a $(B Key) 87 template has(Key...) 88 { 89 static assert(Key.length == 1); 90 enum has = ExpressionList!(get!Key).length > 0; 91 } 92 93 /// Setting values to specific keys (or adding new key-values) 94 template set(KeyValues...) 95 { 96 static assert(KeyValues.length >= 2, "KeyValueList.set is expecting at least one pair!"); 97 static assert(KeyValues.length % 2 == 0, "KeyValuesExpressionList.set is expecting even count of arguments!"); 98 99 template inner(KeyValues...) 100 { 101 static if(KeyValues.length == 2) { 102 static if(is(KeyValues[0])) { 103 alias Key = KeyValues[0]; 104 } else { 105 enum Key = KeyValues[0]; 106 } 107 108 static if(is(KeyValues[1])) { 109 alias Value = KeyValues[1]; 110 } else { 111 enum Value = KeyValues[1]; 112 } 113 114 private template innerFind(T...) 115 { 116 static if(T.length == 0) { 117 alias innerFind = ExpressionList!(Key, Value); 118 } else 119 { 120 static if(is(Key)) { 121 static if(is(T[0] == Key)) { 122 alias innerFind = ExpressionList!(Key, Value, T[2 .. $]); 123 } else { 124 alias innerFind = ExpressionList!(T[0 .. 2], innerFind!(T[2 .. $])); 125 } 126 } else 127 { 128 static if(T[0] == Key) { 129 alias innerFind = ExpressionList!(Key, Value, T[2 .. $]); 130 } else { 131 alias innerFind = ExpressionList!(T[0 .. 2], innerFind!(T[2 .. $])); 132 } 133 } 134 } 135 } 136 137 alias inner = innerFind!Pairs; 138 } else { 139 alias inner = ExpressionList!(inner!(KeyValues[0 .. $/2]), inner!(KeyValues[$/2 .. $])); 140 } 141 } 142 alias set = KeyValueList!(inner!KeyValues); 143 } 144 145 /// Applies $(B F) template for each pair (key-value). 146 template map(alias F) 147 { 148 alias map = KeyValueList!(staticMap2!(F, Pairs)); 149 } 150 151 private static template getKeys(T...) 152 { 153 static if(T.length == 0) { 154 alias getKeys = ExpressionList!(); 155 } else { 156 alias getKeys = ExpressionList!(T[0], getKeys!(T[2 .. $])); 157 } 158 } 159 /// Getting expression list of all keys 160 alias keys = getKeys!Pairs; 161 162 private static template getValues(T...) 163 { 164 static if(T.length == 0) { 165 alias getValues = ExpressionList!(); 166 } else { 167 alias getValues = ExpressionList!(T[1], getValues!(T[2 .. $])); 168 } 169 } 170 /// Getting expression list of all values 171 alias values = getValues!Pairs; 172 173 /** 174 * Filters entries with function or template $(B F), leaving entry only if 175 * $(B F) returning $(B true). 176 */ 177 static template filter(alias F) 178 { 179 alias filter = KeyValueList!(staticFilter2!(F, Pairs)); 180 } 181 182 /** 183 * Filters entries with function or template $(B F) passing only a key from an entry, leaving entry only if 184 * $(B F) returning $(B true). 185 */ 186 static template filterByKey(alias F) 187 { 188 private alias newKeys = staticFilter!(F, keys); 189 private alias newValues = staticMap!(get, newKeys); 190 alias filterByKey = KeyValueList!(staticRobin!(StrictExpressionList!(newKeys, newValues))); 191 } 192 } 193 /// 194 unittest 195 { 196 alias map = KeyValueList!("a", 42, "b", 23); 197 static assert(map.get!"a" == 42); 198 static assert(map.get!("a", "b") == ExpressionList!(42, 23)); 199 static assert(map.get!"c".length == 0); 200 201 alias map2 = KeyValueList!(int, float, float, double, double, 42); 202 static assert(is(map2.get!int == float)); 203 static assert(is(map2.get!float == double)); 204 static assert(map2.get!double == 42); 205 206 static assert(map.has!"a"); 207 static assert(map2.has!int); 208 static assert(!map2.has!void); 209 static assert(!map.has!"c"); 210 211 alias map3 = map.set!("c", 4); 212 static assert(map3.get!"c" == 4); 213 alias map4 = map.set!("c", 4, "d", 8); 214 static assert(map4.get!("c", "d") == ExpressionList!(4, 8)); 215 alias map5 = map.set!("a", 4); 216 static assert(map5.get!"a" == 4); 217 218 template inc(string key, int val) 219 { 220 alias inc = ExpressionList!(key, val+1); 221 } 222 223 alias map6 = map.map!inc; 224 static assert(map6.get!"a" == 43); 225 static assert(map6.get!("a", "b") == ExpressionList!(43, 24)); 226 227 static assert(map.keys == ExpressionList!("a", "b")); 228 static assert(map.values == ExpressionList!(42, 23)); 229 }