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 Sönke Ludwig 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 dutils.data.utils.traits; 11 12 /** 13 Checks if given type is a getter function type 14 15 Returns: `true` if argument is a getter 16 */ 17 template isPropertyGetter(T...) if (T.length == 1) { 18 import std.traits : functionAttributes, FunctionAttribute, ReturnType, isSomeFunction; 19 20 static if (isSomeFunction!(T[0])) { 21 enum isPropertyGetter = (functionAttributes!(T[0]) & FunctionAttribute.property) != 0 22 && !is(ReturnType!T == void); 23 } else 24 enum isPropertyGetter = false; 25 } 26 27 /// 28 unittest { 29 interface Test { 30 @property int getter(); 31 @property void setter(int); 32 int simple(); 33 } 34 35 static assert(isPropertyGetter!(typeof(&Test.getter))); 36 static assert(!isPropertyGetter!(typeof(&Test.setter))); 37 static assert(!isPropertyGetter!(typeof(&Test.simple))); 38 static assert(!isPropertyGetter!int); 39 } 40 41 /** 42 Checks if given type is a setter function type 43 44 Returns: `true` if argument is a setter 45 */ 46 template isPropertySetter(T...) if (T.length == 1) { 47 import std.traits : functionAttributes, FunctionAttribute, ReturnType, isSomeFunction; 48 49 static if (isSomeFunction!(T[0])) { 50 enum isPropertySetter = (functionAttributes!(T) & FunctionAttribute.property) != 0 51 && is(ReturnType!(T[0]) == void); 52 } else 53 enum isPropertySetter = false; 54 } 55 56 /// 57 unittest { 58 interface Test { 59 @property int getter(); 60 @property void setter(int); 61 int simple(); 62 } 63 64 static assert(isPropertySetter!(typeof(&Test.setter))); 65 static assert(!isPropertySetter!(typeof(&Test.getter))); 66 static assert(!isPropertySetter!(typeof(&Test.simple))); 67 static assert(!isPropertySetter!int); 68 } 69 70 /** 71 Deduces single base interface for a type. Multiple interfaces 72 will result in compile-time error. 73 74 Params: 75 T = interface or class type 76 77 Returns: 78 T if it is an interface. If T is a class, interface it implements. 79 */ 80 template baseInterface(T) if (is(T == interface) || is(T == class)) { 81 import std.traits : InterfacesTuple; 82 83 static if (is(T == interface)) { 84 alias baseInterface = T; 85 } else { 86 alias Ifaces = InterfacesTuple!T; 87 static assert(Ifaces.length == 1, 88 "Type must be either provided as an interface or implement only one interface"); 89 alias baseInterface = Ifaces[0]; 90 } 91 } 92 93 /// 94 unittest { 95 interface I1 { 96 } 97 98 class A : I1 { 99 } 100 101 interface I2 { 102 } 103 104 class B : I1, I2 { 105 } 106 107 static assert(is(baseInterface!I1 == I1)); 108 static assert(is(baseInterface!A == I1)); 109 static assert(!is(typeof(baseInterface!B))); 110 } 111 112 /** 113 Determins if a member is a public, non-static data field. 114 */ 115 template isRWPlainField(T, string M) { 116 static if (!isRWField!(T, M)) 117 enum isRWPlainField = false; 118 else { 119 //pragma(msg, T.stringof~"."~M~":"~typeof(__traits(getMember, T, M)).stringof); 120 enum isRWPlainField = __traits(compiles, *(&__traits(getMember, 121 Tgen!T(), M)) = *(&__traits(getMember, Tgen!T(), M))); 122 } 123 } 124 125 /** 126 Determines if a member is a public, non-static, de-facto data field. 127 128 In addition to plain data fields, R/W properties are also accepted. 129 */ 130 template isRWField(T, string M) { 131 import std.traits; 132 import std.typetuple; 133 134 static void testAssign()() { 135 static if (!isCopyable!T) 136 T t; // for structs with disabled copy constructor 137 else 138 T t = *(cast(T*) 0); 139 __traits(getMember, t, M) = __traits(getMember, t, M); 140 } 141 142 // reject type aliases 143 static if (is(TypeTuple!(__traits(getMember, T, M)))) 144 enum isRWField = false; 145 // reject non-public members 146 else static if (!isPublicMember!(T, M)) 147 enum isRWField = false; 148 // reject static members 149 else static if (!isNonStaticMember!(T, M)) 150 enum isRWField = false; 151 // reject non-typed members 152 else static if (!is(typeof(__traits(getMember, T, M)))) 153 enum isRWField = false; 154 // reject void typed members (includes templates) 155 else static if (is(typeof(__traits(getMember, T, M)) == void)) 156 enum isRWField = false; 157 // reject non-assignable members 158 else static if (!__traits(compiles, testAssign!()())) 159 enum isRWField = false; 160 else static if (anySatisfy!(isSomeFunction, __traits(getMember, T, M))) { 161 // If M is a function, reject if not @property or returns by ref 162 private enum FA = functionAttributes!(__traits(getMember, T, M)); 163 enum isRWField = (FA & FunctionAttribute.property) != 0; 164 } else { 165 enum isRWField = true; 166 } 167 } 168 169 unittest { 170 import std.algorithm; 171 172 static struct S { 173 alias a = int; // alias 174 int i; // plain RW field 175 enum j = 42; // manifest constant 176 static int k = 42; // static field 177 private int privateJ; // private RW field 178 179 this(Args...)(Args args) { 180 } 181 182 @disable this(this); 183 184 // read-write property (OK) 185 @property int p1() { 186 return privateJ; 187 } 188 189 @property void p1(int j) { 190 privateJ = j; 191 } 192 // read-only property (NO) 193 @property int p2() { 194 return privateJ; 195 } 196 // write-only property (NO) 197 @property void p3(int value) { 198 privateJ = value; 199 } 200 // ref returning property (OK) 201 @property ref int p4() { 202 return i; 203 } 204 // parameter-less template property (OK) 205 @property ref int p5()() { 206 return i; 207 } 208 // not treated as a property by DMD, so not a field 209 @property int p6()() { 210 return privateJ; 211 } 212 213 @property void p6(int j)() { 214 privateJ = j; 215 } 216 217 static @property int p7() { 218 return k; 219 } 220 221 static @property void p7(int value) { 222 k = value; 223 } 224 225 ref int f1() { 226 return i; 227 } // ref returning function (no field) 228 229 int f2(Args...)(Args args) { 230 return i; 231 } 232 233 ref int f3(Args...)(Args args) { 234 return i; 235 } 236 237 void someMethod() { 238 } 239 240 ref int someTempl()() { 241 return i; 242 } 243 } 244 245 enum plainFields = ["i"]; 246 enum fields = ["i", "p1", "p4", "p5"]; 247 248 foreach (mem; __traits(allMembers, S)) { 249 static if (isRWField!(S, mem)) 250 static assert(fields.canFind(mem), mem ~ " detected as field."); 251 else 252 static assert(!fields.canFind(mem), mem ~ " not detected as field."); 253 254 static if (isRWPlainField!(S, mem)) 255 static assert(plainFields.canFind(mem), mem ~ " not detected as plain field."); 256 else 257 static assert(!plainFields.canFind(mem), mem ~ " not detected as plain field."); 258 } 259 } 260 261 package T Tgen(T)() { 262 return T.init; 263 } 264 265 /** 266 Tests if the protection of a member is public. 267 */ 268 template isPublicMember(T, string M) { 269 import std.algorithm, std.typetuple : TypeTuple; 270 271 static if (!__traits(compiles, TypeTuple!(__traits(getMember, T, M)))) 272 enum isPublicMember = false; 273 else { 274 alias MEM = TypeTuple!(__traits(getMember, T, M)); 275 static if (__traits(compiles, __traits(getProtection, MEM))) 276 enum isPublicMember = __traits(getProtection, MEM).among("public", "export"); 277 else 278 enum isPublicMember = true; 279 } 280 } 281 282 unittest { 283 class C { 284 int a; 285 export int b; 286 protected int c; 287 private int d; 288 package int e; 289 void f() { 290 } 291 292 static void g() { 293 } 294 295 private void h() { 296 } 297 298 private static void i() { 299 } 300 } 301 302 static assert(isPublicMember!(C, "a")); 303 static assert(isPublicMember!(C, "b")); 304 static assert(!isPublicMember!(C, "c")); 305 static assert(!isPublicMember!(C, "d")); 306 static assert(!isPublicMember!(C, "e")); 307 static assert(isPublicMember!(C, "f")); 308 static assert(isPublicMember!(C, "g")); 309 static assert(!isPublicMember!(C, "h")); 310 static assert(!isPublicMember!(C, "i")); 311 312 struct S { 313 int a; 314 export int b; 315 private int d; 316 package int e; 317 } 318 319 static assert(isPublicMember!(S, "a")); 320 static assert(isPublicMember!(S, "b")); 321 static assert(!isPublicMember!(S, "d")); 322 static assert(!isPublicMember!(S, "e")); 323 324 S s; 325 s.a = 21; 326 assert(s.a == 21); 327 } 328 329 /** 330 Tests if a member requires $(D this) to be used. 331 */ 332 template isNonStaticMember(T, string M) { 333 import std.typetuple; 334 import std.traits; 335 336 alias MF = TypeTuple!(__traits(getMember, T, M)); 337 static if (M.length == 0) { 338 enum isNonStaticMember = false; 339 } else static if (anySatisfy!(isSomeFunction, MF)) { 340 enum isNonStaticMember = !__traits(isStaticFunction, MF); 341 } else { 342 enum isNonStaticMember = !__traits(compiles, () { 343 auto x = __traits(getMember, T, M); 344 }()); 345 } 346 } 347 348 unittest { // normal fields 349 struct S { 350 int a; 351 static int b; 352 enum c = 42; 353 void f(); 354 static void g(); 355 ref int h() { 356 return a; 357 } 358 359 static ref int i() { 360 return b; 361 } 362 } 363 364 static assert(isNonStaticMember!(S, "a")); 365 static assert(!isNonStaticMember!(S, "b")); 366 static assert(!isNonStaticMember!(S, "c")); 367 static assert(isNonStaticMember!(S, "f")); 368 static assert(!isNonStaticMember!(S, "g")); 369 static assert(isNonStaticMember!(S, "h")); 370 static assert(!isNonStaticMember!(S, "i")); 371 } 372 373 unittest { // tuple fields 374 struct S(T...) { 375 T a; 376 static T b; 377 } 378 379 alias T = S!(int, float); 380 auto p = T.b; 381 static assert(isNonStaticMember!(T, "a")); 382 static assert(!isNonStaticMember!(T, "b")); 383 384 alias U = S!(); 385 static assert(!isNonStaticMember!(U, "a")); 386 static assert(!isNonStaticMember!(U, "b")); 387 } 388 389 /** 390 Tests if a Group of types is implicitly convertible to a Group of target types. 391 */ 392 bool areConvertibleTo(alias TYPES, alias TARGET_TYPES)() 393 if (isGroup!TYPES && isGroup!TARGET_TYPES) { 394 static assert(TYPES.expand.length == TARGET_TYPES.expand.length); 395 foreach (i, V; TYPES.expand) 396 if (!is(V : TARGET_TYPES.expand[i])) 397 return false; 398 return true; 399 } 400 401 /// Test if the type $(D DG) is a correct delegate for an opApply where the 402 /// key/index is of type $(D TKEY) and the value of type $(D TVALUE). 403 template isOpApplyDg(DG, TKEY, TVALUE) { 404 import std.traits; 405 406 static if (is(DG == delegate) && is(ReturnType!DG : int)) { 407 private alias PTT = ParameterTypeTuple!(DG); 408 private alias PSCT = ParameterStorageClassTuple!(DG); 409 private alias STC = ParameterStorageClass; 410 // Just a value 411 static if (PTT.length == 1) { 412 enum isOpApplyDg = (is(PTT[0] == TVALUE)); 413 } else static if (PTT.length == 2) { 414 enum isOpApplyDg = (is(PTT[0] == TKEY)) && (is(PTT[1] == TVALUE)); 415 } else 416 enum isOpApplyDg = false; 417 } else { 418 enum isOpApplyDg = false; 419 } 420 } 421 422 unittest { 423 static assert(isOpApplyDg!(int delegate(int, string), int, string)); 424 static assert(isOpApplyDg!(int delegate(ref int, ref string), int, string)); 425 static assert(isOpApplyDg!(int delegate(int, ref string), int, string)); 426 static assert(isOpApplyDg!(int delegate(ref int, string), int, string)); 427 } 428 429 // Synchronized statements are logically nothrow but dmd still marks them as throwing. 430 // DMD#4115, Druntime#1013, Druntime#1021, Phobos#2704 431 import core.sync.mutex : Mutex; 432 433 enum synchronizedIsNothrow = __traits(compiles, (Mutex m) nothrow{ 434 synchronized (m) { 435 } 436 }); 437 438 template StripHeadConst(T) { 439 static if (is(T == const(F), F)) 440 alias StripHeadConst = StripHeadConst!F; 441 else static if (is(T == immutable(F), F)) 442 alias StripHeadConst = StripHeadConst!F; 443 else static if (is(T == inout(F), F)) 444 alias StripHeadConst = StripHeadConst!F; 445 else 446 alias StripHeadConst = T; 447 } 448 449 unittest { 450 static assert(is(StripHeadConst!(int) == int)); 451 static assert(is(StripHeadConst!(const(int)) == int)); 452 static assert(is(StripHeadConst!(immutable(int)) == int)); 453 static assert(is(StripHeadConst!(const(immutable(int))) == int)); 454 static assert(is(StripHeadConst!(const(int[])) == const(int)[])); 455 } 456 457 template derivedMethod(C, alias method) { 458 import std.traits : FunctionTypeOf, MemberFunctionsTuple, ParameterTypeTuple; 459 import std.meta : AliasSeq; 460 461 enum fname = __traits(identifier, method); 462 alias overloads = MemberFunctionsTuple!(C, fname); 463 alias PTypes = ParameterTypeTuple!method; 464 465 template impl(size_t i) { 466 static if (i >= overloads.length) 467 alias impl = AliasSeq!(); 468 else { 469 alias FT = FunctionTypeOf!(overloads[i]); 470 static if (__traits(compiles, FT(PTypes.init))) 471 alias impl = overloads[i]; 472 else 473 alias impl = impl!(i + 1); 474 } 475 } 476 477 alias derivedMethod = impl!0; 478 } 479 480 template RecursiveFunctionAttributes(alias func) { 481 import std.meta : AliasSeq, staticMap; 482 import std.traits : BaseTypeTuple; 483 484 static if (is(AliasSeq!(__traits(parent, func))[0])) { 485 alias C = AliasSeq!(__traits(parent, func))[0]; 486 template rimpl(T) { 487 alias DF = derivedMethod!(T, func); 488 static if (AliasSeq!(DF).length > 0) 489 alias rimpl = RecursiveFunctionAttributes!DF; 490 else 491 alias rimpl = AliasSeq!(); 492 } 493 494 alias RecursiveFunctionAttributes = AliasSeq!(__traits(getAttributes, 495 func), staticMap!(rimpl, BaseTypeTuple!C)); 496 } else { 497 alias RecursiveFunctionAttributes = AliasSeq!(__traits(getAttributes, func)); 498 } 499 } 500 501 unittest { 502 interface I { 503 @(1) void test(); 504 } 505 506 interface J { 507 @(4) void test(int); 508 } 509 510 class C : I, J { 511 override @(2) void test() { 512 } 513 514 override void test(int) { 515 } 516 } 517 518 class D : C { 519 override @(3) void test() { 520 } 521 } 522 523 static assert([RecursiveFunctionAttributes!(D.test)] == [3, 2, 1]); 524 }