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_eq( sprintf!"%s == %04d"("1+2", 3), "1+2 == 0003" );
25 assert_eq( sprintf!"%2$s == %1$s"("1+2", 5, 8), "5 == 1+2" );
26 assert_throw!Error( sprintf!"%s%s"(1) );
27 }
28
29 /// Create an exception with automatically completed filename and lineno information
30
31 ExceptionType genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params)
32 {
33 static if( T.length > 0 && is(T[$-1] : Throwable) )
34 return new ExceptionType(params[0..$-1], fn, ln, params[$-1]);
35 else
36 return new ExceptionType(params, fn, ln);
37 }
38
39 unittest
40 {
41 assert_ne( genex!Exception("msg").file, "" );
42 assert_ne( genex!Exception("msg").line, 0 );
43 assert_ne( genex!Exception("msg",new Exception("bar")).next, Exception.init );
44 }
45
46 /// Mixing-in the bean constructor for a class
47
48 /*mixin*/
49 template SimpleConstructor()
50 {
51 /// member-by-member constructor
52 static if( is(typeof(super) == Object) || super.tupleof.length==0 )
53 this( typeof(this.tupleof) params )
54 {
55 static if(this.tupleof.length>0)
56 this.tupleof = params;
57 }
58 else
59 this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
60 {
61 // including (only) the direct super class members
62 // may not always be a desirable choice, but should work for many cases
63 super(ps);
64 static if(this.tupleof.length>0)
65 this.tupleof = params;
66 }
67 }
68
69 unittest
70 {
71 class Temp
72 {
73 int x;
74 string y;
75 mixin SimpleConstructor;
76 }
77 assert_eq( (new Temp(1,"foo")).x, 1 );
78 assert_eq( (new Temp(1,"foo")).y, "foo" );
79 assert( !__traits(compiles, new Temp) );
80 assert( !__traits(compiles, new Temp(1)) );
81 assert( !__traits(compiles, new Temp("foo",1)) );
82
83 class Tomp : Temp
84 {
85 real z;
86 mixin SimpleConstructor;
87 }
88 assert_eq( (new Tomp(1,"foo",2.5)).x, 1 );
89 assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" );
90 assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 );
91 assert( !__traits(compiles, new Tomp(3.14)) );
92
93 // shiyo- desu. Don't use in this way.
94 // Tamp tries to call new Tomp(real) (because it only sees Tomp's members),
95 // but it fails because Tomp takes (int,string,real).
96 assert( !__traits(compiles, {
97 class Tamp : Tomp { mixin SimpleConstructor; }
98 }) );
99 }
100
101 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
102
103 /*mixin*/
104 template SimpleCompare()
105 {
106 override bool opEquals(Object rhs_) const /// member-by-member equality
107 {
108 if( auto rhs = cast(typeof(this))rhs_ )
109 {
110 foreach(i,_; this.tupleof)
111 if( this.tupleof[i] != rhs.tupleof[i] )
112 return false;
113 return true;
114 }
115 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
116 }
117
118 override hash_t toHash() const /// member-by-member hash
119 {
120 hash_t h = 0;
121 foreach(mem; this.tupleof)
122 h += typeid(mem).getHash(&mem);
123 return h;
124 }
125
126 override int opCmp(Object rhs_) const /// member-by-member compare
127 {
128 if( auto rhs = cast(typeof(this))rhs_ )
129 {
130 foreach(i,_; this.tupleof)
131 if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]))
132 return c;
133 return 0;
134 }
135 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
136 }
137 }
138
139 unittest
140 {
141 class Temp
142 {
143 int x;
144 string y;
145 mixin SimpleConstructor;
146 mixin SimpleCompare;
147 }
148 assert_eq( new Temp(1,"foo"), new Temp(1,"foo") );
149 assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash );
150 assert_ne( new Temp(1,"foo"), new Temp(2,"foo") );
151 assert_ne( new Temp(1,"foo"), new Temp(1,"bar") );
152 assert_gt( new Temp(1,"foo"), new Temp(1,"bar") );
153 assert_lt( new Temp(1,"foo"), new Temp(2,"bar") );
154 assert_ge( new Temp(1,"foo"), new Temp(1,"foo") );
155
156 class TempDummy
157 {
158 int x;
159 string y;
160 mixin SimpleConstructor;
161 mixin SimpleCompare;
162 }
163 assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") );
164 assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") );
165 }
166
167 /// Mixing-in a simple toString method
168
169 /*mixin*/
170 template SimpleToString()
171 {
172 /// member-by-member toString
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 }