1 /** 2 Extensions to `std.traits` module of Phobos. Some may eventually make it into Phobos, 3 some are dirty hacks that work only for vibe.d 4 5 Copyright: © 2012 RejectedSoftware e.K. 6 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 7 Authors: Sönke Ludwig, Михаил Страшун 8 */ 9 10 module vson.meta.traits; 11 12 import vson.meta.typetuple; 13 14 15 /** 16 Checks if given type is a getter function type 17 18 Returns: `true` if argument is a getter 19 */ 20 template isPropertyGetter(T...) 21 if (T.length == 1) 22 { 23 import std.traits : functionAttributes, FunctionAttribute, ReturnType, 24 isSomeFunction; 25 static if (isSomeFunction!(T[0])) { 26 enum isPropertyGetter = 27 (functionAttributes!(T[0]) & FunctionAttribute.property) != 0 28 && !is(ReturnType!T == void); 29 } 30 else 31 enum isPropertyGetter = false; 32 } 33 34 /// 35 unittest 36 { 37 interface Test 38 { 39 @property int getter(); 40 @property void setter(int); 41 int simple(); 42 } 43 44 static assert(isPropertyGetter!(typeof(&Test.getter))); 45 static assert(!isPropertyGetter!(typeof(&Test.setter))); 46 static assert(!isPropertyGetter!(typeof(&Test.simple))); 47 static assert(!isPropertyGetter!int); 48 } 49 50 /** 51 Checks if given type is a setter function type 52 53 Returns: `true` if argument is a setter 54 */ 55 template isPropertySetter(T...) 56 if (T.length == 1) 57 { 58 import std.traits : functionAttributes, FunctionAttribute, ReturnType, 59 isSomeFunction; 60 61 static if (isSomeFunction!(T[0])) { 62 enum isPropertySetter = 63 (functionAttributes!(T) & FunctionAttribute.property) != 0 64 && is(ReturnType!(T[0]) == void); 65 } 66 else 67 enum isPropertySetter = false; 68 } 69 70 /// 71 unittest 72 { 73 interface Test 74 { 75 @property int getter(); 76 @property void setter(int); 77 int simple(); 78 } 79 80 static assert(isPropertySetter!(typeof(&Test.setter))); 81 static assert(!isPropertySetter!(typeof(&Test.getter))); 82 static assert(!isPropertySetter!(typeof(&Test.simple))); 83 static assert(!isPropertySetter!int); 84 } 85 86 /** 87 Deduces single base interface for a type. Multiple interfaces 88 will result in compile-time error. 89 90 Params: 91 T = interface or class type 92 93 Returns: 94 T if it is an interface. If T is a class, interface it implements. 95 */ 96 template baseInterface(T) 97 if (is(T == interface) || is(T == class)) 98 { 99 import std.traits : InterfacesTuple; 100 101 static if (is(T == interface)) { 102 alias baseInterface = T; 103 } 104 else 105 { 106 alias Ifaces = InterfacesTuple!T; 107 static assert ( 108 Ifaces.length == 1, 109 "Type must be either provided as an interface or implement only one interface" 110 ); 111 alias baseInterface = Ifaces[0]; 112 } 113 } 114 115 /// 116 unittest 117 { 118 interface I1 { } 119 class A : I1 { } 120 interface I2 { } 121 class B : I1, I2 { } 122 123 static assert (is(baseInterface!I1 == I1)); 124 static assert (is(baseInterface!A == I1)); 125 static assert (!is(typeof(baseInterface!B))); 126 } 127 128 129 /** 130 Determins if a member is a public, non-static data field. 131 */ 132 template isRWPlainField(T, string M) 133 { 134 static if (!isRWField!(T, M)) enum isRWPlainField = false; 135 else { 136 //pragma(msg, T.stringof~"."~M~":"~typeof(__traits(getMember, T, M)).stringof); 137 enum isRWPlainField = __traits(compiles, *(&__traits(getMember, Tgen!T(), M)) = *(&__traits(getMember, Tgen!T(), M))); 138 } 139 } 140 141 /** 142 Determines if a member is a public, non-static, de-facto data field. 143 144 In addition to plain data fields, R/W properties are also accepted. 145 */ 146 template isRWField(T, string M) 147 { 148 import std.traits; 149 import std.typetuple; 150 151 static void testAssign()() { 152 T t = void; 153 __traits(getMember, t, M) = __traits(getMember, t, M); 154 } 155 156 // reject type aliases 157 static if (is(TypeTuple!(__traits(getMember, T, M)))) enum isRWField = false; 158 // reject non-public members 159 else static if (!isPublicMember!(T, M)) enum isRWField = false; 160 // reject static members 161 else static if (!isNonStaticMember!(T, M)) enum isRWField = false; 162 // reject non-typed members 163 else static if (!is(typeof(__traits(getMember, T, M)))) enum isRWField = false; 164 // reject void typed members (includes templates) 165 else static if (is(typeof(__traits(getMember, T, M)) == void)) enum isRWField = false; 166 // reject non-assignable members 167 else static if (!__traits(compiles, testAssign!()())) enum isRWField = false; 168 else static if (anySatisfy!(isSomeFunction, __traits(getMember, T, M))) { 169 // If M is a function, reject if not @property or returns by ref 170 private enum FA = functionAttributes!(__traits(getMember, T, M)); 171 enum isRWField = (FA & FunctionAttribute.property) != 0; 172 } else { 173 enum isRWField = true; 174 } 175 } 176 177 unittest { 178 import std.algorithm; 179 180 struct S { 181 alias a = int; // alias 182 int i; // plain RW field 183 enum j = 42; // manifest constant 184 static int k = 42; // static field 185 private int privateJ; // private RW field 186 187 this(Args...)(Args args) {} 188 189 // read-write property (OK) 190 @property int p1() { return privateJ; } 191 @property void p1(int j) { privateJ = j; } 192 // read-only property (NO) 193 @property int p2() { return privateJ; } 194 // write-only property (NO) 195 @property void p3(int value) { privateJ = value; } 196 // ref returning property (OK) 197 @property ref int p4() { return i; } 198 // parameter-less template property (OK) 199 @property ref int p5()() { return i; } 200 // not treated as a property by DMD, so not a field 201 @property int p6()() { return privateJ; } 202 @property void p6(int j)() { privateJ = j; } 203 204 static @property int p7() { return k; } 205 static @property void p7(int value) { k = value; } 206 207 ref int f1() { return i; } // ref returning function (no field) 208 209 int f2(Args...)(Args args) { return i; } 210 211 ref int f3(Args...)(Args args) { return i; } 212 213 void someMethod() {} 214 215 ref int someTempl()() { return i; } 216 } 217 218 enum plainFields = ["i"]; 219 enum fields = ["i", "p1", "p4", "p5"]; 220 221 foreach (mem; __traits(allMembers, S)) { 222 static if (isRWField!(S, mem)) static assert(fields.canFind(mem), mem~" detected as field."); 223 else static assert(!fields.canFind(mem), mem~" not detected as field."); 224 225 static if (isRWPlainField!(S, mem)) static assert(plainFields.canFind(mem), mem~" not detected as plain field."); 226 else static assert(!plainFields.canFind(mem), mem~" not detected as plain field."); 227 } 228 } 229 230 package T Tgen(T)(){ return T.init; } 231 232 233 /** 234 Tests if the protection of a member is public. 235 */ 236 template isPublicMember(T, string M) 237 { 238 import std.algorithm, std.typetuple : TypeTuple; 239 240 static if (!__traits(compiles, TypeTuple!(__traits(getMember, T, M)))) enum isPublicMember = false; 241 else { 242 alias MEM = TypeTuple!(__traits(getMember, T, M)); 243 enum isPublicMember = __traits(getProtection, MEM).among("public", "export"); 244 } 245 } 246 247 unittest { 248 class C { 249 int a; 250 export int b; 251 protected int c; 252 private int d; 253 package int e; 254 void f() {} 255 static void g() {} 256 private void h() {} 257 private static void i() {} 258 } 259 260 static assert (isPublicMember!(C, "a")); 261 static assert (isPublicMember!(C, "b")); 262 static assert (!isPublicMember!(C, "c")); 263 static assert (!isPublicMember!(C, "d")); 264 static assert (!isPublicMember!(C, "e")); 265 static assert (isPublicMember!(C, "f")); 266 static assert (isPublicMember!(C, "g")); 267 static assert (!isPublicMember!(C, "h")); 268 static assert (!isPublicMember!(C, "i")); 269 270 struct S { 271 int a; 272 export int b; 273 private int d; 274 package int e; 275 } 276 static assert (isPublicMember!(S, "a")); 277 static assert (isPublicMember!(S, "b")); 278 static assert (!isPublicMember!(S, "d")); 279 static assert (!isPublicMember!(S, "e")); 280 281 S s; 282 s.a = 21; 283 assert(s.a == 21); 284 } 285 286 /** 287 Tests if a member requires $(D this) to be used. 288 */ 289 template isNonStaticMember(T, string M) 290 { 291 import std.typetuple; 292 import std.traits; 293 294 alias MF = TypeTuple!(__traits(getMember, T, M)); 295 static if (M.length == 0) { 296 enum isNonStaticMember = false; 297 } else static if (anySatisfy!(isSomeFunction, MF)) { 298 enum isNonStaticMember = !__traits(isStaticFunction, MF); 299 } else { 300 enum isNonStaticMember = !__traits(compiles, (){ auto x = __traits(getMember, T, M); }()); 301 } 302 } 303 304 unittest { // normal fields 305 struct S { 306 int a; 307 static int b; 308 enum c = 42; 309 void f(); 310 static void g(); 311 ref int h() { return a; } 312 static ref int i() { return b; } 313 } 314 static assert(isNonStaticMember!(S, "a")); 315 static assert(!isNonStaticMember!(S, "b")); 316 static assert(!isNonStaticMember!(S, "c")); 317 static assert(isNonStaticMember!(S, "f")); 318 static assert(!isNonStaticMember!(S, "g")); 319 static assert(isNonStaticMember!(S, "h")); 320 static assert(!isNonStaticMember!(S, "i")); 321 } 322 323 unittest { // tuple fields 324 struct S(T...) { 325 T a; 326 static T b; 327 } 328 329 alias T = S!(int, float); 330 auto p = T.b; 331 static assert(isNonStaticMember!(T, "a")); 332 static assert(!isNonStaticMember!(T, "b")); 333 334 alias U = S!(); 335 static assert(!isNonStaticMember!(U, "a")); 336 static assert(!isNonStaticMember!(U, "b")); 337 } 338 339 340 /** 341 Tests if a Group of types is implicitly convertible to a Group of target types. 342 */ 343 bool areConvertibleTo(alias TYPES, alias TARGET_TYPES)() 344 if (isGroup!TYPES && isGroup!TARGET_TYPES) 345 { 346 static assert(TYPES.expand.length == TARGET_TYPES.expand.length); 347 foreach (i, V; TYPES.expand) 348 if (!is(V : TARGET_TYPES.expand[i])) 349 return false; 350 return true; 351 } 352 353 /// Test if the type $(D DG) is a correct delegate for an opApply where the 354 /// key/index is of type $(D TKEY) and the value of type $(D TVALUE). 355 template isOpApplyDg(DG, TKEY, TVALUE) { 356 import std.traits; 357 static if (is(DG == delegate) && is(ReturnType!DG : int)) { 358 private alias PTT = ParameterTypeTuple!(DG); 359 private alias PSCT = ParameterStorageClassTuple!(DG); 360 private alias STC = ParameterStorageClass; 361 // Just a value 362 static if (PTT.length == 1) { 363 enum isOpApplyDg = (is(PTT[0] == TVALUE) && PSCT[0] == STC.ref_); 364 } else static if (PTT.length == 2) { 365 enum isOpApplyDg = (is(PTT[0] == TKEY) && PSCT[0] == STC.ref_) 366 && (is(PTT[1] == TKEY) && PSCT[1] == STC.ref_); 367 } else 368 enum isOpApplyDg = false; 369 } else { 370 enum isOpApplyDg = false; 371 } 372 }