1 /** 2 Utility templates that help working with User Defined Attributes 3 4 Copyright: © 2013 Sönke Ludwig 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig, Михаил Страшун 7 */ 8 9 module dutils.data.utils.uda; 10 11 /** 12 Small convenience wrapper to find and extract certain UDA from given type. 13 Will stop on first element which is of required type. 14 15 Params: 16 UDA = type or template to search for in UDA list 17 Symbol = symbol to query for UDA's 18 allow_types = if set to `false` considers attached `UDA` types an error 19 (only accepts instances/values) 20 21 Returns: aggregated search result struct with 3 field. `value` aliases found UDA. 22 `found` is boolean flag for having a valid find. `index` is integer index in 23 attribute list this UDA was found at. 24 */ 25 template findFirstUDA(alias UDA, alias Symbol, bool allow_types = false) 26 if (!is(UDA)) { 27 enum findFirstUDA = findNextUDA!(UDA, Symbol, 0, allow_types); 28 } 29 30 /// Ditto 31 template findFirstUDA(UDA, alias Symbol, bool allow_types = false) { 32 enum findFirstUDA = findNextUDA!(UDA, Symbol, 0, allow_types); 33 } 34 35 private struct UdaSearchResult(alias UDA) { 36 alias value = UDA; 37 bool found = false; 38 long index = -1; 39 } 40 41 /** 42 Small convenience wrapper to find and extract certain UDA from given type. 43 Will start at the given index and stop on the next element which is of required type. 44 45 Params: 46 UDA = type or template to search for in UDA list 47 Symbol = symbol to query for UDA's 48 idx = 0-based index to start at. Should be positive, and under the total number of attributes. 49 allow_types = if set to `false` considers attached `UDA` types an error 50 (only accepts instances/values) 51 52 Returns: aggregated search result struct with 3 field. `value` aliases found UDA. 53 `found` is boolean flag for having a valid find. `index` is integer index in 54 attribute list this UDA was found at. 55 */ 56 private template findNextUDA(alias UDA, alias Symbol, long idx, bool allow_types = false) 57 if (!is(UDA)) { 58 import std.traits : isInstanceOf; 59 import std.typetuple : TypeTuple; 60 61 private alias udaTuple = TypeTuple!(__traits(getAttributes, Symbol)); 62 63 static assert(idx >= 0, "Index given to findNextUDA can't be negative"); 64 static assert(idx <= udaTuple.length, 65 "Index given to findNextUDA is above the number of attribute"); 66 67 public template extract(size_t index, list...) { 68 static if (!list.length) 69 enum extract = UdaSearchResult!(null)(false, -1); 70 else { 71 static if (is(list[0])) { 72 static if (is(UDA) && is(list[0] == UDA) || !is(UDA) && isInstanceOf!(UDA, list[0])) { 73 static assert(allow_types, "findNextUDA is designed to look up values, not types"); 74 enum extract = UdaSearchResult!(list[0])(true, index); 75 } else 76 enum extract = extract!(index + 1, list[1 .. $]); 77 } else { 78 static if (is(UDA) && is(typeof(list[0]) == UDA) || !is(UDA) 79 && isInstanceOf!(UDA, typeof(list[0]))) { 80 import dutils.data.utils.traits : isPropertyGetter; 81 82 static if (isPropertyGetter!(list[0])) { 83 enum value = list[0]; 84 enum extract = UdaSearchResult!(value)(true, index); 85 } else 86 enum extract = UdaSearchResult!(list[0])(true, index); 87 } else 88 enum extract = extract!(index + 1, list[1 .. $]); 89 } 90 } 91 } 92 93 enum findNextUDA = extract!(idx, udaTuple[idx .. $]); 94 } 95 /// ditto 96 private template findNextUDA(UDA, alias Symbol, long idx, bool allow_types = false) { 97 import std.traits : isInstanceOf; 98 import std.typetuple : TypeTuple; 99 100 private alias udaTuple = TypeTuple!(__traits(getAttributes, Symbol)); 101 102 static assert(idx >= 0, "Index given to findNextUDA can't be negative"); 103 static assert(idx <= udaTuple.length, 104 "Index given to findNextUDA is above the number of attribute"); 105 106 public template extract(size_t index, list...) { 107 static if (!list.length) 108 enum extract = UdaSearchResult!(null)(false, -1); 109 else { 110 static if (is(list[0])) { 111 static if (is(list[0] == UDA)) { 112 static assert(allow_types, "findNextUDA is designed to look up values, not types"); 113 enum extract = UdaSearchResult!(list[0])(true, index); 114 } else 115 enum extract = extract!(index + 1, list[1 .. $]); 116 } else { 117 static if (is(typeof(list[0]) == UDA)) { 118 import dutils.data.utils.traits : isPropertyGetter; 119 120 static if (isPropertyGetter!(list[0])) { 121 enum value = list[0]; 122 enum extract = UdaSearchResult!(value)(true, index); 123 } else 124 enum extract = UdaSearchResult!(list[0])(true, index); 125 } else 126 enum extract = extract!(index + 1, list[1 .. $]); 127 } 128 } 129 } 130 131 enum findNextUDA = extract!(idx, udaTuple[idx .. $]); 132 } 133 134 /// 135 unittest { 136 struct Attribute { 137 int x; 138 } 139 140 @("something", Attribute(42), Attribute(41)) 141 void symbol(); 142 143 enum result0 = findNextUDA!(string, symbol, 0); 144 static assert(result0.found); 145 static assert(result0.index == 0); 146 static assert(result0.value == "something"); 147 148 enum result1 = findNextUDA!(Attribute, symbol, 0); 149 static assert(result1.found); 150 static assert(result1.index == 1); 151 static assert(result1.value == Attribute(42)); 152 153 enum result2 = findNextUDA!(int, symbol, 0); 154 static assert(!result2.found); 155 156 enum result3 = findNextUDA!(Attribute, symbol, result1.index + 1); 157 static assert(result3.found); 158 static assert(result3.index == 2); 159 static assert(result3.value == Attribute(41)); 160 } 161 162 unittest { 163 struct Attribute { 164 int x; 165 } 166 167 @(Attribute) void symbol(); 168 169 static assert(!is(findNextUDA!(Attribute, symbol, 0))); 170 171 enum result0 = findNextUDA!(Attribute, symbol, 0, true); 172 static assert(result0.found); 173 static assert(result0.index == 0); 174 static assert(is(result0.value == Attribute)); 175 } 176 177 unittest { 178 struct Attribute { 179 int x; 180 } 181 182 enum Dummy; 183 184 @property static Attribute getter() { 185 return Attribute(42); 186 } 187 188 @Dummy @getter void symbol(); 189 190 enum result0 = findNextUDA!(Attribute, symbol, 0); 191 static assert(result0.found); 192 static assert(result0.index == 1); 193 static assert(result0.value == Attribute(42)); 194 } 195 196 /// Eager version of findNextUDA that represent all instances of UDA in a Tuple. 197 /// If one of the attribute is a type instead of an instance, compilation will fail. 198 template UDATuple(alias UDA, alias Sym) { 199 import std.typetuple : TypeTuple; 200 201 private template extract(size_t maxSize, Founds...) { 202 private alias LastFound = Founds[$ - 1]; 203 // No more to find 204 static if (!LastFound.found) 205 enum extract = Founds[0 .. $ - 1]; 206 else { 207 // For ease of use, this is a Tuple of UDA, not a tuple of UdaSearchResult!(...) 208 private alias Result = TypeTuple!(Founds[0 .. $ - 1], LastFound.value); 209 // We're at the last parameter 210 static if (LastFound.index == maxSize) 211 enum extract = Result; 212 else 213 enum extract = extract!(maxSize, Result, findNextUDA!(UDA, Sym, LastFound.index + 1)); 214 } 215 } 216 217 private enum maxIndex = TypeTuple!(__traits(getAttributes, Sym)).length; 218 enum UDATuple = extract!(maxIndex, findNextUDA!(UDA, Sym, 0)); 219 } 220 221 unittest { 222 import std.typetuple : TypeTuple; 223 224 struct Attribute { 225 int x; 226 } 227 228 enum Dummy; 229 230 @(Dummy, Attribute(21), Dummy, Attribute(42), Attribute(84)) void symbol() { 231 } 232 233 @(Dummy, Attribute(21), Dummy, Attribute(42), Attribute) void wrong() { 234 } 235 236 alias Cmp = TypeTuple!(Attribute(21), Attribute(42), Attribute(84)); 237 static assert(Cmp == UDATuple!(Attribute, symbol)); 238 static assert(!is(UDATuple!(Attribute, wrong))); 239 }