1 /**
2 * Authors: k.inaba
3 * License: NYSL 0.9982 http://www.kmonos.net/nysl/
4 *
5 * Common tricks and utilities for programming in D.
6 */
7 module tricks.tricks;
8 import tricks.test;
9 import core.exception;
10 import std.array : appender;
11 import std.format : formattedWrite;
12 import std.traits;
13 import std.typetuple;
14
15 /// Simple Wrapper for std.format.doFormat
16
17 string sprintf(string fmt, T...)(T params)
18 {
19 auto writer = appender!string();
20 formattedWrite(writer, fmt, params);
21 return writer.data;
22 }
23
24 unittest
25 {
26 assert_eq( sprintf!"%s == %04d"("1+2", 3), "1+2 == 0003" );
27 assert_eq( sprintf!"%2$s == %1$s"("1+2", 5, 8), "5 == 1+2" );
28 assert_throw!Error( sprintf!"%s%s"(1) );
29 }
30
31 /// Create an exception with automatically completed filename and lineno information
32
33 ExceptionType genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params)
34 {
35 static if( T.length > 0 && is(T[$-1] : Throwable) )
36 return new ExceptionType(params[0..$-1], fn, ln, params[$-1]);
37 else
38 return new ExceptionType(params, fn, ln);
39 }
40
41 unittest
42 {
43 assert_ne( genex!Exception("msg").file, "" );
44 assert_ne( genex!Exception("msg").line, 0 );
45 assert_ne( genex!Exception("msg",new Exception("bar")).next, Exception.init );
46 }
47
48 /// Mixing-in the bean constructor for a class
49
50 /*mixin*/
51 template SimpleConstructor()
52 {
53 /// member-by-member constructor
54 static if( is(typeof(super) == Object) || super.tupleof.length==0 )
55 this( typeof(this.tupleof) params )
56 {
57 static if(this.tupleof.length>0)
58 this.tupleof = params;
59 }
60 else
61 this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
62 {
63 // including (only) the direct super class members
64 // may not always be a desirable choice, but should work for many cases
65 super(ps);
66 static if(this.tupleof.length>0)
67 this.tupleof = params;
68 }
69 }
70
71 unittest
72 {
73 class Temp
74 {
75 int x;
76 string y;
77 mixin SimpleConstructor;
78 }
79 assert_eq( (new Temp(1,"foo")).x, 1 );
80 assert_eq( (new Temp(1,"foo")).y, "foo" );
81 assert( !__traits(compiles, new Temp) );
82 assert( !__traits(compiles, new Temp(1)) );
83 assert( !__traits(compiles, new Temp("foo",1)) );
84
85 class Tomp : Temp
86 {
87 real z;
88 mixin SimpleConstructor;
89 }
90 assert_eq( (new Tomp(1,"foo",2.5)).x, 1 );
91 assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" );
92 assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 );
93 assert( !__traits(compiles, new Tomp(3.14)) );
94
95 // shiyo- desu. Don't use in this way.
96 // Tamp tries to call new Tomp(real) (because it only sees Tomp's members),
97 // but it fails because Tomp takes (int,string,real).
98 assert( !__traits(compiles, {
99 class Tamp : Tomp { mixin SimpleConstructor; }
100 }) );
101 }
102
103 hash_t structuralHash(T)(T x)
104 {
105 alias SC_Unqual!(T) UCT;
106
107 static if(is(UCT == class))
108 return (cast(UCT)x).toHash();
109 else
110 static if(SC_HasGoodHash!(UCT))
111 { return typeid(UCT).getHash(&x); }
112 else
113 static if(is(UCT T == T[]))
114 { hash_t h; foreach(e; x) h+=structuralHash(e); return h; }
115 else
116 static if(is(UCT == struct))
117 static if(__traits(compiles, std.bigint.BigInt))
118 static if(is(UCT == std.bigint.BigInt))
119 return cast(hash_t) x.toInt();
120 else
121 static assert(false, "should not use struct.toHash");
122 else
123 static assert(false, "should not use struct.toHash");
124 else
125 static assert(false, "nonhashable datatype "~UCT.stringof);
126 }
127
128 alias std.traits.Unqual SC_Unqual;
129
130 template SC_HasGoodHash(T)
131 {
132 enum SC_HasGoodHash =
133 is(T : bool) || isNumeric!(T) || isSomeString!(T) || isSomeChar!(T) || isPointer!(T);
134 }
135
136 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
137 /// BE SURE THAT THIS IS CONSISTENT WITH opCmp and opEquals
138
139 template SimpleToHash()
140 {
141 override hash_t toHash() const /// member-by-member hash
142 {
143 hash_t h = 0;
144 foreach(mem; this.tupleof)
145 h += structuralHash(mem);
146 return h;
147 }
148 }
149
150 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
151
152 template SimpleCompareWithoutToHash()
153 {
154 override bool opEquals(Object rhs) const /// member-by-member equality
155 {
156 return opCmp(rhs) == 0;
157 }
158
159 override int opCmp(Object rhs_) const /// member-by-member compare
160 {
161 if( rhs_ is null )
162 return -1;
163 if( auto rhs = cast(typeof(this))rhs_ )
164 {
165 foreach(i,_; this.tupleof)
166 {
167 static if(is(typeof(_) == struct))
168 auto c = (cast(SC_Unqual!(typeof(_)))this.tupleof[i]).opCmp(rhs.tupleof[i]);
169 else
170 auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]);
171 if(c)
172 return c;
173 }
174 return 0;
175 }
176 return typeid(this).opCmp(typeid(rhs_));
177 }
178 }
179
180 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
181
182 /*mixin*/
183 template SimpleCompare()
184 {
185 mixin SimpleToHash;
186 mixin SimpleCompareWithoutToHash;
187 }
188
189 unittest
190 {
191 class Temp
192 {
193 int x;
194 string y;
195 mixin SimpleConstructor;
196 mixin SimpleCompare;
197 }
198 assert_eq( new Temp(1,"foo"), new Temp(1,"foo") );
199 assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash );
200 assert_ne( new Temp(1,"foo"), new Temp(2,"foo") );
201 assert_ne( new Temp(1,"foo"), new Temp(1,"bar") );
202 assert_gt( new Temp(1,"foo"), new Temp(1,"bar") );
203 assert_lt( new Temp(1,"foo"), new Temp(2,"bar") );
204 assert_ge( new Temp(1,"foo"), new Temp(1,"foo") );
205
206 class TempDummy
207 {
208 int x;
209 string y;
210 mixin SimpleConstructor;
211 mixin SimpleCompare;
212 }
213 assert_ne( new Temp(1,"foo"), new TempDummy(1,"foo") );
214 assert_nothrow( new Temp(1,"foo") <= new TempDummy(1,"foo") );
215 }
216
217 /// Mixing-in a simple toString method
218
219 /*mixin*/
220 template SimpleToString()
221 {
222 /// member-by-member toString
223 override string toString()
224 {
225 string str = sprintf!"%s("(typeof(this).stringof);
226 foreach(i,mem; this.tupleof)
227 {
228 if(i) str ~= ",";
229 static if( is(typeof(mem) == std.bigint.BigInt) )
230 str ~= std.bigint.toDecimalString(mem);
231 else
232 str ~= sprintf!"%s"(mem);
233 }
234 return str ~ ")";
235 }
236 }
237
238 version(unittest) import std.bigint;
239 unittest
240 {
241 class Temp
242 {
243 int x;
244 string y;
245 BigInt z;
246 mixin SimpleConstructor;
247 mixin SimpleToString;
248 }
249 assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" );
250 }
251
252 /// Everything is in
253
254 /*mixin*/
255 template SimpleClass()
256 {
257 mixin SimpleConstructor;
258 mixin SimpleCompare;
259 mixin SimpleToString;
260 }
261
262 /// Utility
263
264 template firstParam(T)
265 {
266 alias ParameterTypeTuple!(T)[0] firstParam;
267 }