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 /+
26 using:
27 
28 void func(T)( T v )
29     if( isPseudoInterface(Interface,T) )
30 {
31 }
32 
33  +/
34 module des.util.pseudointerface;
35 
36 import std.traits;
37 
38 bool isPseudoInterface(I,T, bool _assert=true, string FILE=__FILE__, int LINE=__LINE__ )()
39 { 
40     import std.string;
41     import std.conv;
42     bool fail(Args...)( string fmt, Args args )
43     {
44         if( _assert ) assert( 0, FILE ~ "(" ~ to!string(LINE) ~ ") " ~ format( fmt, args ) );
45         else return false;
46     }
47     bool checkMembers( I, T, mem... )()
48     {
49         static if( is(typeof(mem[0]) == string ) && mem.length > 1 ) 
50             return checkMembers!(I,T,mem[0])() && checkMembers!(I,T,mem[1 .. $])();
51         else
52         {
53             // существует ли тип поля mem в I
54             static if( is( typeof( __traits(getMember, I, mem ) ) ) )
55             {
56                 alias typeof( __traits(getMember, I, mem ) ) i;
57 
58                 // если поле "интерфейса" не вызываемое
59                 static if( !isCallable!i ) return true;
60 
61                 // найден ли искомый метод в T
62                 static if( __traits(compiles, __traits(getMember, T, mem) ) )
63                 {
64                     alias typeof(__traits(getMember, T, mem )) t;
65 
66                     static if( !isCallable!t ) 
67                         return fail( "member %s in class %s is not cllable", mem, T.stringof );
68                     else
69                     static if( !is( ReturnType!i == ReturnType!t ) ) 
70                         return fail( "return type of %s in %s must be %s (not %s)", 
71                                         mem, typeid(T), typeid(ReturnType!i), typeid(ReturnType!t) );
72                     else
73                     static if( !is( ParameterTypeTuple!i == ParameterTypeTuple!t ) ) 
74                         return fail( "parameter type tuple of %s in %s must be %s (not %s)",
75                                 mem, typeid(T), typeid(ParameterTypeTuple!i), typeid(ParameterTypeTuple!t) );
76                     else
77                     static if( [ParameterStorageClassTuple!i] != [ParameterStorageClassTuple!t] ) 
78                         return fail( "parameter storage class tuple of %s in %s must be %s (not %s)", 
79                                 mem, typeid(T), to!string(ParameterStorageClassTuple!i),
80                                 to!string(ParameterStorageClassTuple!t) );
81                     else
82                         return true;
83                 }
84                 else return fail( "member %s not found in class %s", mem, typeid(T) );
85             }
86             else return true;
87         }
88     }
89     return checkMembers!(I,T,__traits(allMembers,I))(); 
90 }
91 
92 unittest
93 {
94     interface IFace
95     {
96         void func1( int );
97         size_t func2( string );
98     }
99 
100     struct Afunc1 { void opCall( int ){} }
101     struct Afunc2 { size_t opCall( string ){ return 0; } }
102 
103     class A
104     {
105         Afunc1 func1;
106         Afunc2 func2;
107     }
108 
109     struct B
110     {
111         void func1( int ) { }
112         size_t func2( string str ) { return 0; }
113     }
114 
115     class C
116     {
117         void func1( int ) { }
118         size_t func2( string str ) { return 0; }
119     }
120 
121     class D: A
122     {
123         void func1( int ) { }
124         size_t func2( string str ) { return 0; }
125     }
126 
127     class E
128     {
129         int func1;
130         size_t func2( string str ){ return 0; }
131     }
132 
133     class F
134     {
135         void func1() { }
136         size_t func2( string str ){ return 0; }
137     }
138 
139     class G
140     {
141         void func1( in int ){}
142         size_t func2( string str ){ return 0; }
143     }
144 
145     static assert(  isPseudoInterface!( IFace,A,false ) );
146     static assert(  isPseudoInterface!( IFace,B,false ) );
147     static assert(  isPseudoInterface!( IFace,C,false ) );
148     static assert(  isPseudoInterface!( IFace,D,false ) );
149 
150     static assert(  isPseudoInterface!(A,C,false) );
151 
152     static assert( !isPseudoInterface!( IFace,E,false ) );
153     static assert( !isPseudoInterface!( IFace,F,false ) );
154     static assert( !isPseudoInterface!( IFace,G,false ) );
155 }
156 
157 unittest
158 {
159     interface A
160     {
161         void func1( int );
162     }
163 
164     class B : A
165     {
166         void func1( int ) {}
167         int func2( string ){ return 0; }
168     }
169 
170     struct Cfunc1 { void opCall( int ) { } }
171 
172     interface iB: A
173     {
174         int func2( string );
175     }
176 
177     struct C
178     {
179         Cfunc1 func1;
180         int func2( string ){ return 0; }
181     }
182 
183     // функции базового Object тоже учитываются
184     assert( !isPseudoInterface!( B, C,false ) );
185 
186     assert( isPseudoInterface!( iB, C,false ) );
187 }