1 module des.util.stdext.traits;
2 
3 public
4 {
5     import std.traits;
6     import std.typecons;
7     import std.typetuple;
8 }
9 
10 ///
11 template hasAttrib(alias S,alias f)
12 {
13     enum hasAttrib = impl!(__traits(getAttributes,f));
14 
15     template impl(Attr...)
16     {
17         static if( Attr.length == 0 )
18         {
19             version(tracehasattribimpl) pragma(msg, "empty: ",S,"->",Attr );
20             enum impl=false;
21         }
22         else static if( Attr.length == 1 )
23         {
24             version(tracehasattribimpl) pragma(msg, "single: ",S,"->",Attr );
25             static if( __traits(compiles,typeof(S)) &&
26                        __traits(compiles,typeof(Attr[0])) )
27             {
28                 version(tracehasattribimpl) pragma(msg, "  check as values: ",S,"==",Attr[0] );
29                 enum impl = Attr[0] == S;
30             }
31             else static if( __traits(compiles,is(Attr[0]==S)) )
32             {
33                 version(tracehasattribimpl) pragma(msg, "  check as types: is(",S,"==",Attr[0],")" );
34                 enum impl = is( Attr[0] == S );
35             }
36             else
37             {
38                 version(tracehasattribimpl) pragma(msg, "  no check: false" );
39                 enum impl = false;
40             }
41         }
42         else
43         {
44             version(tracehasattribimpl)
45             {
46                 pragma(msg, "many: ",S,"->",Attr );
47                 pragma(msg, "  p1: ",Attr[0..$/2] );
48                 pragma(msg, "  p2: ",Attr[$/2..$] );
49             }
50             enum impl = impl!(Attr[0..$/2]) || impl!(Attr[$/2..$]);
51         }
52     }
53 }
54 
55 ///
56 unittest
57 {
58     enum clot;
59     size_t zlot(string s){ return s.length; }
60 
61     void fnc1() @clot {}
62     void fnc2() @clot @zlot("ok") {}
63     void fnc3() @zlot("abc") {}
64 
65     static assert(  hasAttrib!(clot,fnc1) );
66     static assert(  hasAttrib!(clot,fnc2) );
67     static assert(  hasAttrib!(2,fnc2) );
68     static assert( !hasAttrib!(clot,fnc3) );
69     static assert(  hasAttrib!(3,fnc3) );
70 }
71 
72 template isString(alias s) { enum isString = is( typeof(s) == string ); }
73 
74 unittest
75 {
76     static assert( isString!"hello" );
77 }
78 
79 ///
80 template staticFilter(alias F, T...)
81 {
82     static if (T.length == 0)
83     {
84         alias staticFilter = TypeTuple!();
85     }
86     else static if (T.length == 1)
87     {
88         static if( F!(T[0]) )
89             alias staticFilter = TypeTuple!(T[0]);
90         else alias staticFilter = TypeTuple!();
91     }
92     else
93     {
94         alias staticFilter =
95             TypeTuple!(
96                 staticFilter!(F, T[ 0  .. $/2]),
97                 staticFilter!(F, T[$/2 ..  $ ]));
98     }
99 }
100 
101 ///
102 struct TemplateVarDef( alias string N, Args... )
103 {
104     alias name = N;
105     alias types = Args;
106 }
107 
108 ///
109 template isTemplateVarDef(T)
110 {
111     enum isTemplateVarDef = is( typeof( impl(T.init) ) );
112     void impl(alias string N, Args...)( TemplateVarDef!(N,Args) x ){}
113 }
114 
115 unittest
116 {
117     static assert( !isTemplateVarDef!float );
118     static assert(  isTemplateVarDef!(TemplateVarDef!("hello", string, int)) );
119 }
120 
121 ///
122 mixin template DefineTemplateVars( alias Trg, List... )
123     if( allSatisfy!( isTemplateVarDef, List ) )
124 {
125     static if( List.length == 0 ) {}
126     else static if( List.length == 1 )
127     {
128         mixin( "Trg!(List[0].types) " ~ List[0].name ~ ";" );
129     }
130     else
131     {
132         mixin DefineTemplateVars!(Trg,List[0..$/2]);
133         mixin DefineTemplateVars!(Trg,List[$/2..$]);
134     }
135 }
136 
137 ///
138 unittest
139 {
140     import std..string;
141 
142     static struct X(Args...)
143     {
144         void func( Args args )
145         {
146             static if( Args.length == 1 && is( Args[0] == string ) )
147                 assert( args[0] == "hello" );
148             else static if( Args.length == 2 && is( Args[0] == float ) && is( Args[1] == int ) )
149             {
150                 import std.math;
151                 assert( abs(args[0] - 3.14) < float.epsilon );
152                 assert( args[1] == 12 );
153             }
154             else static assert(0,"undefined for this unittest");
155         }
156     }
157 
158     static class ZZ
159     {
160         mixin DefineTemplateVars!( X, TemplateVarDef!("ok",string),
161                                       TemplateVarDef!("da",float,int),
162                 );
163     }
164 
165     auto zz = new ZZ;
166     static assert( is( typeof(zz.ok) == X!string ) );
167     static assert( is( typeof(zz.da) == X!(float,int) ) );
168     zz.ok.func( "hello" );
169     zz.da.func( 3.14, 12 );
170 }
171 
172 ///
173 unittest
174 {
175     enum mark;
176 
177     static class A
178     {
179         void s1() @mark {}
180         void f2() {}
181         @mark
182         {
183             void s3( int, string ) {}
184             void s4( float x ) {}
185         }
186     }
187     
188     template isVoidMarked(T)
189     {
190         alias isVoidMarked = isVoidMarkedFunc;
191 
192         template isVoidMarkedFunc(string n)
193         {
194             static if( __traits(compiles,impl!(__traits(getMember,T,n))) )
195                 enum isVoidMarkedFunc = impl!(__traits(getMember,T,n));
196             else enum isVoidMarkedFunc = false;
197 
198             template impl(alias f)
199             {
200                 enum impl = isCallable!f &&
201                             is( ReturnType!f == void ) &&
202                             hasAttrib!(mark,f);
203             }
204         }
205     }
206 
207     template TemplateVarDefFromMethod(T)
208     {
209         template TemplateVarDefFromMethod(string name)
210         {
211             alias TemplateVarDefFromMethod = TemplateVarDef!(name,ParameterTypeTuple!(__traits(getMember,T,name)));
212         }
213     }
214 
215     alias tvd = staticMap!( TemplateVarDefFromMethod!A, staticFilter!(isVoidMarked!A,__traits(allMembers,A)) );
216     static assert( tvd.length == 3 );
217     alias exp = TypeTuple!( TemplateVarDef!("s1"), TemplateVarDef!("s3",int,string), TemplateVarDef!("s4",float) );
218     static assert( is(tvd[0] == exp[0]) );
219     static assert( is(tvd[1] == exp[1]) );
220     static assert( is(tvd[2] == exp[2]) );
221 }
222 
223 /++
224 using:
225 
226 void func(T)( T v )
227     if( isPseudoInterface(Interface,T) )
228 {
229 }
230  +/
231 bool isPseudoInterface(I,T, bool _assert=true, string FILE=__FILE__, int LINE=__LINE__ )()
232 { 
233     import std..string;
234     import std.conv;
235     bool fail(Args...)( string fmt, Args args )
236     {
237         if( _assert ) assert( 0, FILE ~ "(" ~ to!string(LINE) ~ ") " ~ format( fmt, args ) );
238         else return false;
239     }
240     bool checkMembers( I, T, mem... )()
241     {
242         static if( is(typeof(mem[0]) == string ) && mem.length > 1 ) 
243             return checkMembers!(I,T,mem[0])() && checkMembers!(I,T,mem[1 .. $])();
244         else
245         {
246             static if( is( typeof( __traits(getMember, I, mem ) ) ) )
247             {
248                 alias typeof( __traits(getMember, I, mem ) ) i;
249 
250                 static if( !isCallable!i ) return true;
251 
252                 static if( __traits(compiles, __traits(getMember, T, mem) ) )
253                 {
254                     alias typeof(__traits(getMember, T, mem )) t;
255 
256                     static if( !isCallable!t ) 
257                         return fail( "member %s in class %s is not cllable", mem, T.stringof );
258                     else
259                     static if( !is( ReturnType!i == ReturnType!t ) ) 
260                         return fail( "return type of %s in %s must be %s (not %s)", 
261                                         mem, typeid(T), typeid(ReturnType!i), typeid(ReturnType!t) );
262                     else
263                     static if( !is( ParameterTypeTuple!i == ParameterTypeTuple!t ) ) 
264                         return fail( "parameter type tuple of %s in %s must be %s (not %s)",
265                                 mem, typeid(T), typeid(ParameterTypeTuple!i), typeid(ParameterTypeTuple!t) );
266                     else
267                     static if( [ParameterStorageClassTuple!i] != [ParameterStorageClassTuple!t] ) 
268                         return fail( "parameter storage class tuple of %s in %s must be %s (not %s)", 
269                                 mem, typeid(T), to!string(ParameterStorageClassTuple!i),
270                                 to!string(ParameterStorageClassTuple!t) );
271                     else
272                         return true;
273                 }
274                 else return fail( "member %s not found in class %s", mem, typeid(T) );
275             }
276             else return true;
277         }
278     }
279     return checkMembers!(I,T,__traits(allMembers,I))(); 
280 }
281 
282 ///
283 unittest
284 {
285     interface IFace
286     {
287         void func1( int );
288         size_t func2( string );
289     }
290 
291     struct Afunc1 { void opCall( int ){} }
292     struct Afunc2 { size_t opCall( string ){ return 0; } }
293 
294     class A
295     {
296         Afunc1 func1;
297         Afunc2 func2;
298     }
299 
300     struct B
301     {
302         void func1( int ) { }
303         size_t func2( string str ) { return 0; }
304     }
305 
306     class C
307     {
308         void func1( int ) { }
309         size_t func2( string str ) { return 0; }
310     }
311 
312     class D: A
313     {
314         void func1( int ) { }
315         size_t func2( string str ) { return 0; }
316     }
317 
318     class E
319     {
320         int func1;
321         size_t func2( string str ){ return 0; }
322     }
323 
324     class F
325     {
326         void func1() { }
327         size_t func2( string str ){ return 0; }
328     }
329 
330     class G
331     {
332         void func1( in int ){}
333         size_t func2( string str ){ return 0; }
334     }
335 
336     static assert(  isPseudoInterface!( IFace,A,false ) );
337     static assert(  isPseudoInterface!( IFace,B,false ) );
338     static assert(  isPseudoInterface!( IFace,C,false ) );
339     static assert(  isPseudoInterface!( IFace,D,false ) );
340 
341     static assert(  isPseudoInterface!(A,C,false) );
342 
343     static assert( !isPseudoInterface!( IFace,E,false ) );
344     static assert( !isPseudoInterface!( IFace,F,false ) );
345     static assert( !isPseudoInterface!( IFace,G,false ) );
346 }
347 
348 unittest
349 {
350     interface A
351     {
352         void func1( int );
353     }
354 
355     class B : A
356     {
357         void func1( int ) {}
358         int func2( string ){ return 0; }
359     }
360 
361     struct Cfunc1 { void opCall( int ) { } }
362 
363     interface iB: A
364     {
365         int func2( string );
366     }
367 
368     struct C
369     {
370         Cfunc1 func1;
371         int func2( string ){ return 0; }
372     }
373 
374     assert( !isPseudoInterface!( B, C,false ) );
375     assert(  isPseudoInterface!( iB, C,false ) );
376 }