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 std.array : appender;
10 import std.format : formattedWrite;
11 import core.exception : AssertError;
12
13 /// Simple Wrapper for std.format.doFormat
14
15 string sprintf(string fmt, T...)(T params)
16 {
17 auto writer = appender!string();
18 formattedWrite(writer, fmt, params);
19 return writer.data;
20 }
21
22 unittest
23 {
24 assert( sprintf!"%s == %d"("1+2", 3) == "1+2 == 3" );
25 assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" );
26 }
27
28 /// Create an exception with automatically completed filename and lineno information
29
30 auto genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params)
31 {
32 static if( T.length > 0 && is(T[$-1] : Throwable) )
33 return new ExceptionType(params[0..$-1], fn, ln, params[$-1]);
34 else
35 return new ExceptionType(params, fn, ln);
36 }
37
38 unittest
39 {
40 assert_ne( genex!Exception("msg").file, "" );
41 assert_ne( genex!Exception("msg").line, 0 );
42 assert_ne( genex!Exception("msg",new Exception("bar")).next, Exception.init );
43 }
44
45 /// Mixing-in the bean constructor for a class
46
47 /*mixin*/
48 template SimpleConstructor()
49 {
50 static if( is(typeof(super) == Object) || super.tupleof.length==0 )
51 this( typeof(this.tupleof) params )
52 {
53 static if(this.tupleof.length>0)
54 this.tupleof = params;
55 }
56 else
57 this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
58 {
59 // including (only) the direct super class members
60 // may not always be a desirable choice, but should work for many cases
61 super(ps);
62 static if(this.tupleof.length>0)
63 this.tupleof = params;
64 }
65 }
66
67 unittest
68 {
69 class Temp
70 {
71 int x;
72 string y;
73 mixin SimpleConstructor;
74 }
75 assert_eq( (new Temp(1,"foo")).x, 1 );
76 assert_eq( (new Temp(1,"foo")).y, "foo" );
77 assert( !__traits(compiles, new Temp) );
78 assert( !__traits(compiles, new Temp(1)) );
79 assert( !__traits(compiles, new Temp("foo",1)) );
80
81 class Tomp : Temp
82 {
83 real z;
84 mixin SimpleConstructor;
85 }
86 assert_eq( (new Tomp(1,"foo",2.5)).x, 1 );
87 assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" );
88 assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 );
89 assert( !__traits(compiles, new Tomp(3.14)) );
90
91 // shiyo- desu. Don't use in this way.
92 // Tamp tries to call new Tomp(real) (because it only sees Tomp's members),
93 // but it fails because Tomp takes (int,string,real).
94 assert( !__traits(compiles, {
95 class Tamp : Tomp
96 {
97 mixin SimpleConstructor;
98 }
99 }) );
100 }
101
102 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
103
104 /*mixin*/
105 template SimpleCompare()
106 {
107 override bool opEquals(Object rhs_) const
108 {
109 if( auto rhs = cast(typeof(this))rhs_ )
110 {
111 foreach(i,_; this.tupleof)
112 if( this.tupleof[i] != rhs.tupleof[i] )
113 return false;
114 return true;
115 }
116 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
117 }
118
119 override hash_t toHash() const
120 {
121 hash_t h = 0;
122 foreach(mem; this.tupleof)
123 h += typeid(mem).getHash(&mem);
124 return h;
125 }
126
127 override int opCmp(Object rhs_) const
128 {
129 if( auto rhs = cast(typeof(this))rhs_ )
130 {
131 foreach(i,_; this.tupleof)
132 if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]))
133 return c;
134 return 0;
135 }
136 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
137 }
138 }
139
140 unittest
141 {
142 class Temp
143 {
144 int x;
145 string y;
146 mixin SimpleConstructor;
147 mixin SimpleCompare;
148 }
149 assert_eq( new Temp(1,"foo"), new Temp(1,"foo") );
150 assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash );
151 assert_ne( new Temp(1,"foo"), new Temp(2,"foo") );
152 assert_ne( new Temp(1,"foo"), new Temp(1,"bar") );
153 assert_gt( new Temp(1,"foo"), new Temp(1,"bar") );
154 assert_lt( new Temp(1,"foo"), new Temp(2,"bar") );
155 assert_ge( new Temp(1,"foo"), new Temp(1,"foo") );
156
157 class TempDummy
158 {
159 int x;
160 string y;
161 mixin SimpleConstructor;
162 mixin SimpleCompare;
163 }
164 assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") );
165 assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") );
166 }
167
168 /// Mixing-in a simple toString method
169
170 /*mixin*/
171 template SimpleToString()
172 {
173 override string toString()
174 {
175 string str = sprintf!"%s("(typeof(this).stringof);
176 foreach(i,mem; this.tupleof)
177 {
178 if(i) str ~= ",";
179 static if( is(typeof(mem) == std.bigint.BigInt) )
180 str ~= std.bigint.toDecimalString(mem);
181 else
182 str ~= sprintf!"%s"(mem);
183 }
184 return str ~ ")";
185 }
186 }
187
188 version(unittest) import std.bigint;
189 unittest
190 {
191 class Temp
192 {
193 int x;
194 string y;
195 BigInt z;
196 mixin SimpleConstructor;
197 mixin SimpleToString;
198 }
199 assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" );
200 }
201
202 /// Everything is in
203
204 /*mixin*/
205 template SimpleClass()
206 {
207 mixin SimpleConstructor;
208 mixin SimpleCompare;
209 mixin SimpleToString;
210 }