1 module painlesstraits; 2 3 import std.traits; 4 5 package alias Identity(alias A) = A; 6 7 template hasAnnotation(alias f, alias Attr) 8 { 9 import std.typetuple : anySatisfy, TypeTuple; 10 11 alias allAnnotations = TypeTuple!(__traits(getAttributes, f)); 12 template hasMatch(alias attr) 13 { 14 static if (is(Attr)) 15 { 16 alias hasMatch = Identity!(is(typeof(attr) == Attr) || is(attr == Attr)); 17 } 18 else 19 { 20 alias hasMatch = Identity!(is(attr == Attr)); 21 } 22 } 23 24 enum bool hasAnnotation = anySatisfy!(hasMatch, allAnnotations); 25 } 26 27 unittest 28 { 29 enum FooUDA; 30 enum BarUDA; 31 @FooUDA int x; 32 33 static assert(hasAnnotation!(x, FooUDA)); 34 static assert(!hasAnnotation!(x, BarUDA)); 35 } 36 37 template hasAnyOfTheseAnnotations(alias f, Attr...) 38 { 39 enum bool hasAnyOfTheseAnnotations = (function() { 40 bool any = false; 41 foreach (annotation; Attr) 42 { 43 any |= hasAnnotation!(f, annotation); 44 } 45 return any; 46 })(); 47 } 48 49 version (unittest) 50 { 51 // Some types are outside unittest block since old compilers couldn't find them otherwise 52 // This might indicate brittleness of these functions but I'm not sure how to fix that 53 enum AnyFooUDA; 54 enum AnyBazUDA; 55 enum AnyQuxUDA; 56 struct AnyBarUDA 57 { 58 int data; 59 } 60 61 @AnyFooUDA @(AnyBarUDA(1)) int anyx; 62 @(AnyBarUDA(1)) int anyy; 63 @AnyFooUDA int anyz; 64 } 65 66 unittest 67 { 68 static assert(!hasAnyOfTheseAnnotations!(anyy, AnyBazUDA, AnyQuxUDA)); 69 static assert(!hasAnyOfTheseAnnotations!(anyx, AnyBazUDA)); 70 static assert(!hasAnyOfTheseAnnotations!(anyx, AnyQuxUDA)); 71 static assert(hasAnyOfTheseAnnotations!(anyz, AnyFooUDA, AnyBarUDA)); 72 static assert(hasAnyOfTheseAnnotations!(anyx, AnyBarUDA, AnyQuxUDA)); 73 } 74 75 template hasValueAnnotation(alias f, alias Attr) 76 { 77 import std.typetuple : anySatisfy, TypeTuple; 78 79 alias allAnnotations = TypeTuple!(__traits(getAttributes, f)); 80 alias hasMatch(alias attr) = Identity!(is(Attr) && is(typeof(attr) == Attr)); 81 enum bool hasValueAnnotation = anySatisfy!(hasMatch, allAnnotations); 82 } 83 84 unittest 85 { 86 enum FooUDA; 87 struct BarUDA 88 { 89 int data; 90 } 91 92 @FooUDA int x; 93 @FooUDA @(BarUDA(1)) int y; 94 95 static assert(!hasValueAnnotation!(x, BarUDA)); 96 static assert(!hasValueAnnotation!(x, FooUDA)); 97 static assert(hasValueAnnotation!(y, BarUDA)); 98 static assert(!hasValueAnnotation!(y, FooUDA)); 99 } 100 101 template hasAnyOfTheseValueAnnotations(alias f, Attr...) 102 { 103 enum bool hasAnyOfTheseValueAnnotations = (function() { 104 bool any = false; 105 foreach (annotation; Attr) 106 { 107 any |= hasValueAnnotation!(f, annotation); 108 } 109 return any; 110 })(); 111 } 112 113 unittest 114 { 115 static assert(!hasAnyOfTheseValueAnnotations!(anyz, AnyBarUDA)); 116 static assert(!hasAnyOfTheseValueAnnotations!(anyz, AnyBarUDA, AnyFooUDA)); 117 static assert(hasAnyOfTheseValueAnnotations!(anyx, AnyBarUDA)); 118 static assert(!hasAnyOfTheseValueAnnotations!(anyx, AnyFooUDA)); 119 static assert(hasAnyOfTheseValueAnnotations!(anyx, AnyFooUDA, AnyBarUDA)); 120 static assert(hasAnyOfTheseValueAnnotations!(anyy, AnyBarUDA, AnyFooUDA)); 121 } 122 123 template getAnnotation(alias f, Attr) 124 { 125 static if (hasValueAnnotation!(f, Attr)) 126 { 127 enum getAnnotation = (function() { 128 foreach (attr; __traits(getAttributes, f)) 129 static if (is(typeof(attr) == Attr)) 130 return attr; 131 assert(0); 132 })(); 133 } 134 else 135 static assert(0); 136 } 137 138 unittest 139 { 140 static assert(getAnnotation!(anyy, AnyBarUDA).data == 1); 141 } 142 143 template isField(alias T) 144 { 145 enum isField = (function() { 146 // isTemplate is relatively new only use it if it exists 147 static if (__traits(compiles, __traits(isTemplate, T))) 148 return (!isSomeFunction!(T) && !__traits(isTemplate, T)); 149 else 150 return (!isSomeFunction!(T)); 151 })(); 152 } 153 154 unittest 155 { 156 struct Foo 157 { 158 @property auto property() 159 { 160 return 1.0; 161 } 162 163 auto func() 164 { 165 return 1.0; 166 } 167 168 auto tmplt()() 169 { 170 return 1.0; 171 } 172 173 auto field = 0; 174 } 175 176 static assert(!isField!(Foo.property)); 177 static assert(!isField!(Foo.func)); 178 179 static if (__traits(compiles, __traits(isTemplate, Foo.tmplt))) 180 { 181 static assert(!isField!(Foo.tmplt)); 182 static assert(!isSomeFunction!(Foo.tmplt)); 183 } 184 185 static assert(isField!(Foo.field)); 186 187 // Make sure the struct behaves as expected 188 Foo foo; 189 assert(foo.property == 1.0); 190 assert(foo.func() == 1.0); 191 assert(foo.tmplt() == 1.0); 192 assert(foo.field == 0.0); 193 } 194 195 template isFieldOrProperty(alias T) 196 { 197 enum isFieldOrProperty = (function() { 198 static if (isField!(T)) 199 { 200 return true; 201 } 202 else static if (isSomeFunction!(T)) 203 { 204 return (functionAttributes!(T) & FunctionAttribute.property); 205 } 206 else 207 return false; 208 })(); 209 } 210 211 unittest 212 { 213 struct Foo 214 { 215 int success; 216 int failure(int x) 217 { 218 return x; 219 } 220 } 221 222 static assert(isFieldOrProperty!(Foo.success)); 223 static assert(!isFieldOrProperty!(Foo.failure)); 224 225 // Make sure the struct behaves as expected 226 Foo foo; 227 assert(foo.failure(1) == 1); 228 } 229 230 private template publicMemberFilter(T, alias filter) 231 { 232 template Filter(alias memberName) 233 { 234 static if (is(T == class)) 235 { 236 static if (is(typeof(__traits(getMember, T, memberName)))) 237 { 238 static if (isSomeFunction!(__traits(getMember, T, memberName))) 239 { 240 static if (!__traits(compiles, { 241 auto foo = __traits(getMember, T, memberName); 242 })) 243 { 244 enum Filter = filter!(__traits(getMember, T, memberName)); 245 } 246 else 247 enum Filter = false; 248 } 249 else 250 { 251 static if (!__traits(compiles, { 252 auto foo = &__traits(getMember, T, memberName); 253 })) 254 { 255 enum Filter = filter!(__traits(getMember, T, memberName)); 256 } 257 else 258 enum Filter = false; 259 } 260 } 261 else 262 enum Filter = false; 263 } 264 else 265 { 266 static if (is(typeof(__traits(getMember, T.init, memberName)))) 267 { 268 static if (__traits(compiles, { 269 enum Foo = __traits(getMember, T.init, memberName); 270 })) 271 { 272 enum Filter = filter!(__traits(getMember, T.init, memberName)); 273 } 274 else 275 enum Filter = false; 276 } 277 else 278 enum Filter = false; 279 } 280 } 281 } 282 283 // from std.meta to support older compilers 284 private template AliasSeq(TList...) 285 { 286 alias AliasSeq = TList; 287 } 288 289 private template Filter(alias pred, TList...) 290 { 291 static if (TList.length == 0) 292 { 293 alias Filter = AliasSeq!(); 294 } 295 else static if (TList.length == 1) 296 { 297 static if (pred!(TList[0])) 298 alias Filter = AliasSeq!(TList[0]); 299 else 300 alias Filter = AliasSeq!(); 301 } 302 else 303 { 304 alias Filter = AliasSeq!(Filter!(pred, TList[0 .. $ / 2]), Filter!(pred, TList[$ / 2 .. $])); 305 } 306 } 307 308 template allPublicFieldsOrProperties(T) 309 { 310 enum allPublicFieldsOrProperties = Filter!(publicMemberFilter!(T, 311 isFieldOrProperty).Filter, __traits(allMembers, T)); 312 static assert(allPublicFieldsOrProperties.length > 0, "No properties for type " ~ T.stringof); 313 } 314 315 template allPublicFields(T) 316 { 317 enum allPublicFields = Filter!(publicMemberFilter!(T, isField).Filter, __traits(allMembers, T)); 318 static assert(allPublicFields.length > 0, "No properties for type " ~ T.stringof); 319 } 320 321 unittest 322 { 323 import std.typecons : Tuple; 324 325 // toString is template, should be ignored 326 static assert(!isFieldOrProperty!(Tuple!(int)(0).toString)); 327 }