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