root/trunk/minid/utils.d

Revision 426, 11.9 kB (checked in by JarrettBillingsley, 3 weeks ago)

Cleaned up minid.utils a bit. Documented (kind of) the binding library. These are just the DDocs; I intend on having a much more comprehensive tutorial on the binding library later. Also regen'ed the docs.

Line 
1 /******************************************************************************
2 A module holding a variety of utility functions used throughout MiniD.  This
3 module doesn't (and shouldn't) depend on the rest of the library in any way,
4 and as such can't hold implementation-specific functionality.  For that, look
5 in the minid.misc module.
6
7 License:
8 Copyright (c) 2008 Jarrett Billingsley
9
10 This software is provided 'as-is', without any express or implied warranty.
11 In no event will the authors be held liable for any damages arising from the
12 use of this software.
13
14 Permission is granted to anyone to use this software for any purpose,
15 including commercial applications, and to alter it and redistribute it freely,
16 subject to the following restrictions:
17
18     1. The origin of this software must not be misrepresented; you must not
19     claim that you wrote the original software. If you use this software in a
20     product, an acknowledgment in the product documentation would be
21     appreciated but is not required.
22
23     2. Altered source versions must be plainly marked as such, and must not
24     be misrepresented as being the original software.
25
26     3. This notice may not be removed or altered from any source distribution.
27 ******************************************************************************/
28
29 module minid.utils;
30
31 import tango.core.Traits;
32 import tango.core.Tuple;
33 import tango.text.convert.Utf;
34 import tango.text.Util;
35
36 /**
37 See if a string starts with another string.  Useful.
38 */
39 public bool startsWith(T)(T[] string, T[] pattern)
40 {
41     return string.length >= pattern.length && string[0 .. pattern.length] == pattern[];
42 }
43
44 /**
45 See if a string ends with another string.  Also useful.
46 */
47 public bool endsWith(T)(T[] string, T[] pattern)
48 {
49     return string.length >= pattern.length && string[$ - pattern.length .. $] == pattern[];
50 }
51
52 /**
53 Compare two values, a and b, using < and >.  Returns -1 if a < b, 1 if a > b, and 0 otherwise.
54 */
55 public int Compare3(T)(T a, T b)
56 {
57     return a < b ? -1 : a > b ? 1 : 0;
58 }
59
60 /**
61 Compares char[] strings stupidly (just by character value, not lexicographically).
62 */
63 public int scmp(char[] s1, char[] s2)
64 {
65     auto len = s1.length;
66
67     if(s2.length < len)
68         len = s2.length;
69
70     auto result = mismatch(s1.ptr, s2.ptr, len);
71
72     if(result == len)
73         return Compare3(s1.length, s2.length);
74     else
75         return Compare3(s1[result], s2[result]);
76 }
77
78 /**
79 Verifies that the given UTF-8 string is well-formed and returns the length in codepoints.
80 */
81 public size_t verify(char[] s)
82 {
83     size_t ret = 0;
84
85     foreach(dchar c; s)
86         ret++;
87
88     return ret;
89 }
90
91 /**
92 Slice a UTF-8 string using codepoint indices.
93 */
94 public char[] uniSlice(char[] s, size_t lo, size_t hi)
95 {
96     if(lo == hi)
97         return null;
98
99     auto tmp = s;
100     uint realLo = 0;
101
102     for(size_t i = 0; i < lo; i++)
103     {
104         uint ate = 0;
105         decode(tmp, ate);
106         tmp = tmp[ate .. $];
107         realLo += ate;
108     }
109
110     uint realHi = realLo;
111
112     for(size_t i = lo; i < hi; i++)
113     {
114         uint ate = 0;
115         decode(tmp, ate);
116         tmp = tmp[ate .. $];
117         realHi += ate;
118     }
119
120     return s[realLo .. realHi];
121 }
122
123 /**
124 Get the character in a UTF-8 string at the given codepoint index.
125 */
126 public dchar uniCharAt(char[] s, size_t idx)
127 {
128     auto tmp = s;
129     uint ate = 0;
130
131     for(size_t i = 0; i < idx; i++)
132     {
133         decode(tmp, ate);
134         tmp = tmp[ate .. $];
135     }
136
137     return decode(tmp, ate);
138 }
139
140 /**
141 Convert a codepoint index into a UTF-8 string into a byte index.
142 */
143 public size_t uniCPIdxToByte(char[] s, size_t fake)
144 {
145     auto tmp = s;
146     uint ate = 0;
147
148     for(size_t i = 0; i < fake; i++)
149     {
150         decode(tmp, ate);
151         tmp = tmp[ate .. $];
152     }
153
154     return tmp.ptr - s.ptr;
155 }
156
157 /**
158 Metafunction to see if a given type is one of char[], wchar[] or dchar[].
159 */
160 public template isStringType(T)
161 {
162     const bool isStringType = is(T : char[]) || is(T : wchar[]) || is(T : dchar[]);
163 }
164
165 /**
166 Sees if a type is an array.
167 */
168 public template isArrayType(T)
169 {
170     const bool isArrayType = false;
171 }
172
173 public template isArrayType(T : T[])
174 {
175     const bool isArrayType = true;
176 }
177
178 /**
179 Sees if a type is an associative array.
180 */
181 public template isAAType(T)
182 {
183     const bool isAAType = is(typeof(T.init.values[0])[typeof(T.init.keys[0])] == T);
184 }
185
186 /**
187 Get to the bottom of any chain of typedefs!  Returns the first non-typedef'ed type.
188 */
189 public template realType(T)
190 {
191     static if(is(T Base == typedef) || is(T Base == enum))
192         alias realType!(Base) realType;
193     else
194         alias T realType;
195 }
196
197 unittest
198 {
199     assert(isStringType!(char[]));
200     assert(isStringType!(wchar[]));
201     assert(isStringType!(dchar[]));
202     assert(!isStringType!(int));
203     assert(!isStringType!(Object));
204
205     assert(isArrayType!(int[]));
206     assert(isArrayType!(char[]));
207     assert(isArrayType!(int[3][4]));
208     assert(!isArrayType!(int[int]));
209     assert(!isArrayType!(Object));
210
211     typedef int X;
212     typedef X Y;
213    
214     assert(is(realType!(X) == int));
215     assert(is(realType!(Y) == int));
216 }
217
218 /**
219 Make a FOURCC code out of a four-character string.  This is I guess for little-endian platforms..
220 */
221 public template FOURCC(char[] name)
222 {
223     static assert(name.length == 4, "FOURCC's parameter must be 4 characters");
224     const uint FOURCC = (cast(uint)name[3] << 24) | (cast(uint)name[2] << 16) | (cast(uint)name[1] << 8) | cast(uint)name[0];
225 }
226
227 /**
228 Make a version with the major number in the upper 16 bits and the minor in the lower 16 bits.
229 */
230 public template MakeVersion(uint major, uint minor)
231 {
232     const uint MakeVersion = (major << 16) | minor;
233 }
234
235 /**
236 Gets the name of a function alias.
237 */
238 public template NameOfFunc(alias f)
239 {
240     version(LDC)
241         const char[] NameOfFunc = (&f).stringof[1 .. $];
242     else
243         const char[] NameOfFunc = (&f).stringof[2 .. $];
244 }
245
246 debug
247 {
248     private void _foo_(){}
249     static assert(NameOfFunc!(_foo_) == "_foo_", "Oh noes, NameOfFunc needs to be updated.");
250 }
251
252 /**
253 Given a predicate template and a tuple, sorts the tuple.  I'm not sure how quick it is, but it's probably fast enough
254 for sorting most tuples, which hopefully won't be that long.  The predicate template should take two parameters of the
255 same type as the tuple's elements, and return <0 for A < B, 0 for A == B, and >0 for A > B (just like opCmp).
256 */
257 public template QSort(alias Pred, List...)
258 {
259     static if(List.length == 0 || List.length == 1)
260         alias List QSort;
261     else static if(List.length == 2)
262     {
263         static if(Pred!(List[0], List[1]) <= 0)
264             alias Tuple!(List[0], List[1]) QSort;
265         else
266             alias Tuple!(List[1], List[0]) QSort;
267     }
268     else
269         alias Tuple!(QSort!(Pred, QSort_less!(Pred, List)), QSort_equal!(Pred, List), List[0], QSort!(Pred, QSort_greater!(Pred, List))) QSort;
270 }
271
272 private template QSort_less(alias Pred, List...)
273 {
274     static if(List.length == 0 || List.length == 1)
275         alias Tuple!() QSort_less;
276     else static if(Pred!(List[1], List[0]) < 0)
277         alias Tuple!(List[1], QSort_less!(Pred, List[0], List[2 .. $])) QSort_less;
278     else
279         alias QSort_less!(Pred, List[0], List[2 .. $]) QSort_less;
280 }
281
282 private template QSort_equal(alias Pred, List...)
283 {
284     static if(List.length == 0 || List.length == 1)
285         alias Tuple!() QSort_equal;
286     else static if(Pred!(List[1], List[0]) == 0)
287         alias Tuple!(List[1], QSort_equal!(Pred, List[0], List[2 .. $])) QSort_equal;
288     else
289         alias QSort_equal!(Pred, List[0], List[2 .. $]) QSort_equal;
290 }
291
292 private template QSort_greater(alias Pred, List...)
293 {
294     static if(List.length == 0 || List.length == 1)
295         alias Tuple!() QSort_greater;
296     else static if(Pred!(List[1], List[0]) > 0)
297         alias Tuple!(List[1], QSort_greater!(Pred, List[0], List[2 .. $])) QSort_greater;
298     else
299         alias QSort_greater!(Pred, List[0], List[2 .. $]) QSort_greater;
300 }
301
302 /**
303 A useful template that somehow is in Phobos but no Tango.  Sees if a tuple is composed
304 entirely of expressions or aliases.
305 */
306 public template isExpressionTuple(T...)
307 {
308     static if (is(void function(T)))
309         const bool isExpressionTuple = false;
310     else
311         const bool isExpressionTuple = true;
312 }
313
314 // Buggy.
315
316 // template isFinalImpl(T, char[] funcName)
317 // {
318 //  alias ParameterTupleOf!(mixin(T.stringof ~ "." ~ funcName)) _Params;
319 //  alias ReturnTypeOf!(mixin(T.stringof ~ "." ~ funcName)) _ReturnType;
320 //  mixin("alias typeof(new class T { override _ReturnType " ~ funcName ~
321 //      "(_Params _params) { return super." ~ funcName ~ "(_params); } }) res;");
322 // }
323 //
324 // /**
325 // Given a class type and a method name, tells whether that method is final or not.
326 // Thanks Tomasz Stachowiak.
327 // */
328 // template isFinal(T, char[] funcName)
329 // {
330 //  pragma(msg, isFinalImpl!(T, funcName).res.stringof);
331 //  const bool isFinal = !is(isFinalImpl!(T, funcName).res);
332 // }
333 //
334 // private void unit_test()
335 // {
336 //  static class Foo
337 //  {
338 //      final void func1(int a, float b) {}
339 //      void func2(int a, float b) {}
340 //
341 //      final char[] func3(int a, float b) { return null; }
342 //      char[] func4(int a, float b) { return null; }
343 //  }
344 //
345 //  static assert(isFinal!(Foo, "func1"));
346 //  static assert(!isFinal!(Foo, "func2"));
347 //  static assert(isFinal!(Foo, "func3"));
348 //  static assert(!isFinal!(Foo, "func4"));
349 // }
350
351 /**
352 For a given struct, gets a tuple of the names of its fields.
353
354 I have absolutely no idea if what I'm doing here is in any way legal.  I more or less discovered
355 that the compiler gives access to this info in odd cases, and am just exploiting that.  It would
356 be fantastic if the compiler would just tell us these things, but alas, we have to rely on
357 seemingly-buggy undefined behavior.  Sigh.
358 */
359 public template FieldNames(S, int idx = 0)
360 {
361     static if(idx >= S.tupleof.length)
362         alias Tuple!() FieldNames;
363     else
364         alias Tuple!(GetLastName!(S.tupleof[idx].stringof), FieldNames!(S, idx + 1)) FieldNames;
365 }
366
367 private template GetLastName(char[] fullName, int idx = fullName.length - 1)
368 {
369     static if(idx < 0)
370         const char[] GetLastName = fullName;
371     else static if(fullName[idx] == '.')
372         const char[] GetLastName = fullName[idx + 1 .. $];
373     else
374         const char[] GetLastName = GetLastName!(fullName, idx - 1);
375 }
376
377 /**
378 Given an alias to a function, this will give the minimum legal number of arguments it can be called with.
379 Even works for aliases to class methods.  Note, however, that this isn't smart enough to detect the difference
380 between, say, "void foo(int x, int y = 10)" and "void foo(int x) ... void foo(int x, int y)".  There might
381 be a difference, though, so be cautions.
382 */
383 public template MinArgs(alias func)
384 {
385     const uint MinArgs = MinArgsImpl!(func, 0, InitsOf!(ParameterTupleOf!(typeof(&func))));
386 }
387
388 private template MinArgsImpl(alias func, int index, Args...)
389 {
390     static if(index >= Args.length)
391         const uint MinArgsImpl = Args.length;
392     else static if(is(typeof(func(Args[0 .. index]))))
393         const uint MinArgsImpl = index;
394     else
395         const uint MinArgsImpl = MinArgsImpl!(func, index + 1, Args);
396 }
397
398 /**
399 Given a type tuple, this will give an expression tuple of all the .init values for each type.
400 */
401 public template InitsOf(T...)
402 {
403     static if(T.length == 0)
404         alias Tuple!() InitsOf;
405     else
406         alias Tuple!(InitOf!(T[0]), InitsOf!(T[1 .. $])) InitsOf;
407 }
408
409 // BUG 1667
410 private T InitOf_shim(T)()
411 {
412     T t;
413     return t;
414 }
415
416 // This template exists for the sole reason that T.init doesn't work for structs inside templates due
417 // to a forward declaration error.
418 private template InitOf(T)
419 {
420     static if(!is(typeof(Tuple!(T.init))))
421         alias Tuple!(InitOf_shim!(T)()) InitOf;
422     else
423         alias Tuple!(T.init) InitOf;
424 }
425
426 /**
427 Given a class or struct type, gets its name.  This really only exists to mask potential oddities with the
428 way the compiler reports this info (for example, DMD used to insert a space before struct names, but that
429 no longer seems to happen..).
430 */
431 public template NameOfType(T)
432 {
433     const char[] NameOfType = T.stringof;
434 }
435
436 debug
437 {
438     private class _Fribble_ {}
439     private struct _Frobble_ {}
440
441     static assert(NameOfType!(_Fribble_) == "_Fribble_", "NameOfType doesn't work for classes (got " ~ NameOfType!(_Fribble_) ~ ")");
442     static assert(NameOfType!(_Frobble_) == "_Frobble_", "NameOfType doesn't work for structs (got " ~ NameOfType!(_Frobble_) ~ ")");
443 }
Note: See TracBrowser for help on using the browser.