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