1 module des.stdx.traits; 2 3 public 4 { 5 import std.traits; 6 import std.typecons; 7 import std.typetuple; 8 } 9 10 /// returns `bool` value 11 template isComplex(T) 12 { 13 alias Unqual!T UT; 14 enum isComplex = is( UT == cfloat ) || 15 is( UT == cdouble ) || 16 is( UT == creal ); 17 } 18 19 /// 20 unittest 21 { 22 static assert( isComplex!(typeof(4+3i)) ); 23 static assert( !isComplex!(typeof(3i)) ); 24 } 25 26 /// returns `bool` value 27 template isImaginary(T) 28 { 29 alias Unqual!T UT; 30 enum isImaginary = is( UT == ifloat ) || 31 is( UT == idouble ) || 32 is( UT == ireal ); 33 } 34 35 /// 36 unittest 37 { 38 static assert( isImaginary!(typeof(3i)) ); 39 static assert( !isImaginary!(typeof(4+3i)) ); 40 } 41 42 /// 43 template canUseAsArray(R) 44 { 45 import std.range : hasLength; 46 enum canUseAsArray = is(typeof( 47 (inout int = 0) 48 { 49 R r = R.init; 50 auto e = r[0]; 51 static assert( hasLength!R ); 52 static assert( !isNarrowString!R ); 53 })); 54 } 55 56 /// 57 unittest 58 { 59 static assert( canUseAsArray!(int[]) ); 60 static assert( canUseAsArray!(int[3]) ); 61 static assert( canUseAsArray!(float[]) ); 62 63 static struct Vec0 { float[] data; alias data this; } 64 static assert( canUseAsArray!Vec0 ); 65 66 static struct Vec1 { float[] data; } 67 static assert( !canUseAsArray!Vec1 ); 68 69 static struct Vec2 { float[3] data; alias data this; } 70 static assert( canUseAsArray!Vec2 ); 71 72 static assert( !canUseAsArray!(string) ); 73 static assert( !canUseAsArray!(wstring) ); 74 static assert( !canUseAsArray!int ); 75 static assert( !canUseAsArray!float ); 76 static assert( !canUseAsArray!(immutable(void)[]) ); 77 } 78 79 /++ checks has type T basic math operations 80 + 81 + * `isAssignable!(Unqual!T,T)` 82 + * `is( typeof( T.init + T.init ) == T )` 83 + * `is( typeof( T.init - T.init ) == T )` 84 + * `is( typeof( ( T.init * 0.5 ) ) : T )` 85 + * `is( typeof( ( T.init / 0.5 ) ) : T )` 86 +/ 87 template hasBasicMathOp(T) 88 { 89 enum hasBasicMathOp = 90 isAssignable!(Unqual!T,T) && 91 is( typeof( T.init + T.init ) == T ) && 92 is( typeof( T.init - T.init ) == T ) && 93 is( typeof( ( T.init * 0.5 ) ) : T ) && 94 is( typeof( ( T.init / 0.5 ) ) : T ); 95 } 96 97 /// 98 unittest 99 { 100 static assert( !hasBasicMathOp!int ); // can't get int from expr(int * double) 101 static assert( hasBasicMathOp!float ); 102 static assert( hasBasicMathOp!double ); 103 static assert( hasBasicMathOp!real ); 104 static assert( hasBasicMathOp!cfloat ); 105 static assert( !hasBasicMathOp!char ); 106 static assert( !hasBasicMathOp!string ); 107 108 static struct TTest 109 { 110 float x,y; 111 auto opBinary(string op)( in TTest v ) const 112 if( op=="+" || op=="-" ) 113 { return TTest(x+v.x,y+v.y); } 114 auto opBinary(string op)( double v ) const 115 if( op=="*" || op=="/" ) 116 { return TTest(x*v,y*v); } 117 } 118 119 static assert( hasBasicMathOp!TTest ); 120 121 struct FTest 122 { 123 float x,y; 124 auto opAdd( in TTest v ) const { return TTest(x+v.x,y+v.y); } 125 } 126 127 static assert( !hasBasicMathOp!FTest ); 128 } 129 130 /// 131 template hasAttrib(alias S,alias f) 132 { 133 enum hasAttrib = impl!(__traits(getAttributes,f)); 134 135 template impl(Attr...) 136 { 137 static if( Attr.length == 0 ) 138 { 139 version(tracehasattribimpl) pragma(msg, "empty: ",S,"->",Attr ); 140 enum impl=false; 141 } 142 else static if( Attr.length == 1 ) 143 { 144 version(tracehasattribimpl) pragma(msg, "single: ",S,"->",Attr ); 145 static if( __traits(compiles,typeof(S)) && 146 __traits(compiles,typeof(Attr[0])) ) 147 { 148 version(tracehasattribimpl) pragma(msg, " check as values: ",S,"==",Attr[0] ); 149 enum impl = Attr[0] == S; 150 } 151 else static if( __traits(compiles,is(Attr[0]==S)) ) 152 { 153 version(tracehasattribimpl) pragma(msg, " check as types: is(",S,"==",Attr[0],")" ); 154 enum impl = is( Attr[0] == S ); 155 } 156 else 157 { 158 version(tracehasattribimpl) pragma(msg, " no check: false" ); 159 enum impl = false; 160 } 161 } 162 else 163 { 164 version(tracehasattribimpl) 165 { 166 pragma(msg, "many: ",S,"->",Attr ); 167 pragma(msg, " p1: ",Attr[0..$/2] ); 168 pragma(msg, " p2: ",Attr[$/2..$] ); 169 } 170 enum impl = impl!(Attr[0..$/2]) || impl!(Attr[$/2..$]); 171 } 172 } 173 } 174 175 /// 176 unittest 177 { 178 enum clot; 179 size_t zlot(string s){ return s.length; } 180 181 void fnc1() @clot {} 182 void fnc2() @clot @zlot("ok") {} 183 void fnc3() @zlot("abc") {} 184 185 static assert( hasAttrib!(clot,fnc1) ); 186 static assert( hasAttrib!(clot,fnc2) ); 187 static assert( hasAttrib!(2,fnc2) ); 188 static assert( !hasAttrib!(clot,fnc3) ); 189 static assert( hasAttrib!(3,fnc3) ); 190 191 @clot int var1; 192 @zlot("op") string var2; 193 static assert( hasAttrib!(clot,var1) ); 194 static assert( hasAttrib!(2,var2) ); 195 } 196 197 /// 198 template staticFilter(alias F, T...) 199 { 200 static if (T.length == 0) 201 { 202 alias staticFilter = TypeTuple!(); 203 } 204 else static if (T.length == 1) 205 { 206 static if( F!(T[0]) ) 207 alias staticFilter = TypeTuple!(T[0]); 208 else alias staticFilter = TypeTuple!(); 209 } 210 else 211 { 212 alias staticFilter = 213 TypeTuple!( 214 staticFilter!(F, T[ 0 .. $/2]), 215 staticFilter!(F, T[$/2 .. $ ])); 216 } 217 } 218 219 /// 220 struct TemplateVarDef( alias string N, Args... ) 221 { 222 alias name = N; 223 alias types = Args; 224 } 225 226 /// 227 template isTemplateVarDef(T) 228 { 229 enum isTemplateVarDef = is( typeof( impl(T.init) ) ); 230 void impl(alias string N, Args...)( TemplateVarDef!(N,Args) x ){} 231 } 232 233 unittest 234 { 235 static assert( !isTemplateVarDef!float ); 236 static assert( isTemplateVarDef!(TemplateVarDef!("hello", string, int)) ); 237 } 238 239 /// 240 mixin template DefineTemplateVars( alias Trg, List... ) 241 if( allSatisfy!( isTemplateVarDef, List ) ) 242 { 243 static if( List.length == 0 ) {} 244 else static if( List.length == 1 ) 245 { 246 mixin( "Trg!(List[0].types) " ~ List[0].name ~ ";" ); 247 } 248 else 249 { 250 mixin DefineTemplateVars!(Trg,List[0..$/2]); 251 mixin DefineTemplateVars!(Trg,List[$/2..$]); 252 } 253 } 254 255 /// 256 unittest 257 { 258 import std..string; 259 260 static struct X(Args...) 261 { 262 void func( Args args ) 263 { 264 static if( Args.length == 1 && is( Args[0] == string ) ) 265 assert( args[0] == "hello" ); 266 else static if( Args.length == 2 && is( Args[0] == float ) && is( Args[1] == int ) ) 267 { 268 import std.math; 269 assert( abs(args[0] - 3.14) < float.epsilon ); 270 assert( args[1] == 12 ); 271 } 272 else static assert(0,"undefined for this unittest"); 273 } 274 } 275 276 static class ZZ 277 { 278 mixin DefineTemplateVars!( X, TemplateVarDef!("ok",string), 279 TemplateVarDef!("da",float,int), 280 ); 281 } 282 283 auto zz = new ZZ; 284 static assert( is( typeof(zz.ok) == X!string ) ); 285 static assert( is( typeof(zz.da) == X!(float,int) ) ); 286 zz.ok.func( "hello" ); 287 zz.da.func( 3.14, 12 ); 288 } 289 290 /// 291 unittest 292 { 293 enum mark; 294 295 static class A 296 { 297 void s1() @mark {} 298 void f2() {} 299 @mark 300 { 301 void s3( int, string ) {} 302 void s4( float x ) {} 303 } 304 } 305 306 template isVoidMarked(T) 307 { 308 alias isVoidMarked = isVoidMarkedFunc; 309 310 template isVoidMarkedFunc(string n) 311 { 312 static if( __traits(compiles,impl!(__traits(getMember,T,n))) ) 313 enum isVoidMarkedFunc = impl!(__traits(getMember,T,n)); 314 else enum isVoidMarkedFunc = false; 315 316 template impl(alias f) 317 { 318 enum impl = isCallable!f && 319 is( ReturnType!f == void ) && 320 hasAttrib!(mark,f); 321 } 322 } 323 } 324 325 template TemplateVarDefFromMethod(T) 326 { 327 template TemplateVarDefFromMethod(string name) 328 { 329 alias TemplateVarDefFromMethod = TemplateVarDef!(name,ParameterTypeTuple!(__traits(getMember,T,name))); 330 } 331 } 332 333 alias tvd = staticMap!( TemplateVarDefFromMethod!A, staticFilter!(isVoidMarked!A,__traits(allMembers,A)) ); 334 static assert( tvd.length == 3 ); 335 alias exp = TypeTuple!( TemplateVarDef!("s1"), TemplateVarDef!("s3",int,string), TemplateVarDef!("s4",float) ); 336 static assert( is(tvd[0] == exp[0]) ); 337 static assert( is(tvd[1] == exp[1]) ); 338 static assert( is(tvd[2] == exp[2]) ); 339 } 340 341 /++ 342 using: 343 344 void func(T)( T v ) 345 if( isPseudoInterface(Interface,T) ) 346 { 347 } 348 +/ 349 template isPseudoInterface(I,T, bool _assert=true, string FILE=__FILE__, int LINE=__LINE__ ) 350 { 351 import std..string; 352 import std.conv; 353 bool fail(Args...)( string fmt, Args args ) 354 { 355 if( _assert ) assert( 0, FILE ~ "(" ~ to!string(LINE) ~ "): " ~ format( fmt, args ) ); 356 else return false; 357 } 358 bool checkMembers( I, T, mem... )() 359 { 360 static if( is(typeof(mem[0]) == string ) && mem.length > 1 ) 361 return checkMembers!(I,T,mem[0])() && checkMembers!(I,T,mem[1 .. $])(); 362 else 363 { 364 static if( is( typeof( __traits(getMember, I, mem ) ) ) ) 365 { 366 alias typeof( __traits(getMember, I, mem ) ) i; 367 368 static if( !isCallable!i ) return true; 369 370 static if( __traits(compiles, __traits(getMember, T, mem) ) ) 371 { 372 alias typeof(__traits(getMember, T, mem )) t; 373 374 static if( !isCallable!t ) 375 return fail( "member %s in class %s is not cllable", mem, T.stringof ); 376 else 377 static if( !is( ReturnType!i == ReturnType!t ) ) 378 return fail( "return type of %s in %s must be %s (not %s)", 379 mem, typeid(T), typeid(ReturnType!i), typeid(ReturnType!t) ); 380 else 381 static if( !is( ParameterTypeTuple!i == ParameterTypeTuple!t ) ) 382 return fail( "parameter type tuple of %s in %s must be %s (not %s)", 383 mem, typeid(T), typeid(ParameterTypeTuple!i), typeid(ParameterTypeTuple!t) ); 384 else 385 static if( [ParameterStorageClassTuple!i] != [ParameterStorageClassTuple!t] ) 386 return fail( "parameter storage class tuple of %s in %s must be %s (not %s)", 387 mem, typeid(T), to!string(ParameterStorageClassTuple!i), 388 to!string(ParameterStorageClassTuple!t) ); 389 else 390 return true; 391 } 392 else return fail( "member %s not found in class %s", mem, typeid(T) ); 393 } 394 else return true; 395 } 396 } 397 enum isPseudoInterface = checkMembers!(I,T,__traits(allMembers,I))(); 398 } 399 400 /// 401 unittest 402 { 403 interface IFace 404 { 405 void func1( int ); 406 size_t func2( string ); 407 } 408 409 struct Afunc1 { void opCall( int ){} } 410 struct Afunc2 { size_t opCall( string ){ return 0; } } 411 412 class A 413 { 414 Afunc1 func1; 415 Afunc2 func2; 416 } 417 418 struct B 419 { 420 void func1( int ) { } 421 size_t func2( string str ) { return 0; } 422 } 423 424 class C 425 { 426 void func1( int ) { } 427 size_t func2( string str ) { return 0; } 428 } 429 430 class D: A 431 { 432 void func1( int ) { } 433 size_t func2( string str ) { return 0; } 434 } 435 436 class E 437 { 438 int func1; 439 size_t func2( string str ){ return 0; } 440 } 441 442 class F 443 { 444 void func1() { } 445 size_t func2( string str ){ return 0; } 446 } 447 448 class G 449 { 450 void func1( in int ){} 451 size_t func2( string str ){ return 0; } 452 } 453 454 static assert( isPseudoInterface!( IFace,A,false ) ); 455 static assert( isPseudoInterface!( IFace,B,false ) ); 456 static assert( isPseudoInterface!( IFace,C,false ) ); 457 static assert( isPseudoInterface!( IFace,D,false ) ); 458 459 static assert( isPseudoInterface!(A,C,false) ); 460 461 static assert( !isPseudoInterface!( IFace,E,false ) ); 462 static assert( !isPseudoInterface!( IFace,F,false ) ); 463 static assert( !isPseudoInterface!( IFace,G,false ) ); 464 } 465 466 /// 467 unittest 468 { 469 interface A 470 { 471 void func1( int ); 472 } 473 474 class B : A 475 { 476 void func1( int ) {} 477 int func2( string ){ return 0; } 478 } 479 480 struct Cfunc1 { void opCall( int ) { } } 481 482 interface iB: A 483 { 484 int func2( string ); 485 } 486 487 struct C 488 { 489 Cfunc1 func1; 490 int func2( string ){ return 0; } 491 } 492 493 assert( !isPseudoInterface!( B, C,false ) ); 494 assert( isPseudoInterface!( iB, C,false ) ); 495 }