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 }