PageRenderTime 48ms CodeModel.GetById 8ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

/std/typelist.d

http://github.com/jcd/phobos
D | 456 lines | 265 code | 43 blank | 148 comment | 40 complexity | f843ac460db8a2de6d819571ae7b1ad5 MD5 | raw file
  1// Written in the D programming language.
  2
  3/**
  4 * This module defines a list of types $(D_PARAM TypeList)
  5 * and operations on $(D_PARAM TypeList)s.
  6 * Together they define a compile-time functional programming framework,
  7 * complete with lambdas, higher-order functions, and arbitrary data structures
  8 *
  9 * Macros:
 10 *    WIKI = Phobos/StdTypelist
 11 *
 12 * Synopsis:
 13 *
 14 * ----
 15 * // **** BUG **** problems with mutual recursion
 16 * template Synopsis(T...)
 17 * {
 18 *     alias TypeList!(T) list;
 19 *
 20 *     template IsPtr(U) {
 21 *         static if (is(U foo: V*, V))
 22 *             enum IsPtr = true;
 23 *         else
 24 *             enum IsPtr = false;
 25 *     }
 26 *     enum arePointers = All!(list, IsPtr);
 27 *
 28 *     alias Map!(StripPtr, list) StripPointers;
 29 * }
 30 * static assert(is (Synopsis!(char**, void***).StripPointers.toTuple == TypeTuple!(char, void)));
 31 * ----
 32 *
 33 * Copyright: Copyright Bartosz Milewski 2008- 2009.
 34 * License:   <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
 35 * Authors:   $(WEB bartoszmilewski.wordpress.com, Bartosz Milewski)
 36 * Source:    $(PHOBOSSRC std/_typelist.d)
 37 */
 38/*          Copyright Burton Radons 2008 - 2009.
 39 * Distributed under the Boost Software License, Version 1.0.
 40 *    (See accompanying file LICENSE_1_0.txt or copy at
 41 *          http://www.boost.org/LICENSE_1_0.txt)
 42 */
 43module std.typelist;
 44version(unittest) {
 45    import std.typetuple;
 46}
 47
 48/**
 49 * Creates a compile-time list of types from a tuple.
 50 * $(D TypeList)s are more general than tuples because
 51 * you can pass more than one $(D TypeList) to a template.
 52 * You may also combine them into higher-order structures.
 53 * $(D TypeList)s are passed to other templates as alias parameters
 54 * To create an empty list use $(D TypeList!())
 55 *
 56 * $(D TypeList) efines several "methods":
 57 *
 58 * $(D_PARAM toTuple), $(D_PARAM head), $(D_PARAM tail), $(D_PARAM length), $(D_PARAM isEmpty)
 59 *
 60 * Example:
 61 * ---
 62 * template Filter(alias Pred, alias List)
 63 * {
 64 *     static if (List.isEmpty)
 65 *         alias TypeList!() Filter;
 66 *     else static if (Pred!(List.head))
 67 *         alias Cons!(List.head, Filter!(Pred, List.tail)) Filter;
 68 *     else
 69 *         alias Filter!(Pred, List.tail) Filter;
 70 * }
 71 * ---
 72 */
 73
 74template TypeList(T...)
 75{
 76    alias T toTuple;
 77
 78    static if(T.length != 0)
 79    {
 80        alias T[0] head;
 81        alias TypeList!(T[1..$]) tail;
 82        enum length = T.length;
 83        enum isEmpty = false;
 84    }
 85    else
 86    {
 87        enum length = 0;
 88        enum isEmpty = true;
 89    }
 90}
 91
 92unittest {
 93    static assert (is (TypeList!(void*, int).toTuple == TypeTuple!(void*, int)));
 94    static assert (is (TypeList!(void*, int).head == void*));
 95    static assert (is (TypeList!(void*, int).tail.toTuple == TypeTuple!(int)));
 96    static assert (is (TypeList!(int).tail.toTuple == TypeTuple!()));
 97    static assert (TypeList!(int).tail.isEmpty);
 98
 99    static assert (TypeList!(void*, int).length == 2);
100    static assert (!TypeList!(void*, int).isEmpty);
101    static assert (TypeList!().length == 0);
102    static assert (TypeList!().isEmpty);
103}
104
105/**
106 * Appends a type tuple to a $(D TypeList), returns a $(D TypeList)
107*/
108template AppendTypes(alias List, T...)
109{
110    static if (List.isEmpty)
111        alias TypeList!(T) AppendTypes;
112    else
113        alias TypeList!(List.toTuple, T) AppendTypes;
114}
115
116unittest {
117    static assert (is (AppendTypes!(TypeList!(void*, int), long, short).toTuple
118                       == TypeTuple!(void*, int, long, short)));
119    static assert (is (AppendTypes!(TypeList!(void*, int)).toTuple
120                       == TypeTuple!(void*, int)));
121    static assert (AppendTypes!(TypeList!()).isEmpty);
122}
123
124/**
125 * Appends one $(D TypeList) to another, returns a $(D TypeList)
126*/
127template Append(alias Left, alias Right)
128{
129    alias AppendTypes!(Left, Right.toTuple) Append;
130}
131
132unittest {
133    static assert (is (Append!(TypeList!(void*, int), TypeList!(long, short)).toTuple
134                       == TypeTuple!(void*, int, long, short)));
135    static assert (is (Append!(TypeList!(void*, int), TypeList!()).toTuple
136                       == TypeTuple!(void*, int)));
137    static assert (Append!(TypeList!(), TypeList!()).isEmpty);
138}
139
140/**
141 * Prepends a type to a $(D TypeList), returns a $(D TypeList)
142*/
143template Cons(T, alias List)
144{
145    static if (List.isEmpty)
146        alias TypeList!(T) Cons;
147    else
148        alias TypeList!(T, List.toTuple) Cons;
149}
150
151unittest {
152    static assert (is (Cons!(long, TypeList!(void*, int)).toTuple
153                       == TypeTuple!(long, void*, int)));
154    static assert (is (Cons!(long, TypeList!(void*, int)).head
155                       == long));
156    static assert (is (Cons!(int, TypeList!()).toTuple == TypeTuple!(int)));
157    static assert (is (Cons!(char[], Cons!(int, TypeList!())).toTuple
158                    == TypeTuple!(char[], int)));
159}
160
161/**
162 * Tests if all emements of a $(D TypeList) against a predicate.
163 * Returns true if all all types satisfy the predicate, false otherwise.
164*/
165template All(alias List, alias F)
166{
167    static if (List.isEmpty)
168        enum All = true;
169    else
170        enum All = F!(List.head) && All!(List.tail, F);
171}
172
173version(unittest) {
174    template IsPointer(T)
175    {
176        static if (is (T foo: U*, U))
177            enum IsPointer = true;
178        else
179            enum IsPointer = false;
180    }
181}
182
183unittest {
184    static assert (All!(TypeList!(void*, char*, int**), IsPointer));
185    static assert (!All!(TypeList!(void*, char*, int), IsPointer));
186}
187
188/**
189 * Tests if there is an emement in a $(D TypeList) that satisfies a predicate.
190*/
191template Any(alias List, alias F)
192{
193    static if (List.isEmpty)
194        enum Any = false;
195    else
196        enum Any = F!(List.head) || Any!(List.tail, F);
197}
198
199unittest {
200    static assert (Any!(TypeList!(int, char*, int**), IsPointer));
201    static assert (!Any!(TypeList!(char[], char, int), IsPointer));
202}
203
204/**
205 * Applies a given "function" on types to a type tuple. Returns a tuple of results
206*/
207template Map(alias F, T...)
208{
209    alias Map!(F, TypeList!(T)).toTuple Map;
210}
211
212/**
213 * Applies a given "function" to a $(D TypeList). Returns a $(D TypeList) of results
214*/
215private template Map(alias F, alias List)
216{
217    static if (List.isEmpty)
218        alias TypeList!() Map;
219    else
220        alias Cons!(F!(List.head), Map!(F, List.tail)) Map;
221}
222
223version(unittest) {
224    template MakePtr(T)
225    {
226        alias T* MakePtr;
227    }
228}
229
230unittest {
231    static assert (is (MakePtr!(int) == int*));
232    static assert (is (Map!(MakePtr, void *, char) == TypeTuple!(void**, char*)));
233}
234
235/**
236 * Filters a type tuple using a predicate.
237 * Takes a predicate and a tuple and returns another tuple
238*/
239template Filter(alias Pred, T...)
240{
241    alias Filter!(Pred, TypeList!(T)).toTuple Filter;
242}
243
244/**
245 * Filters a $(D TypeList) using a predicate. Returns a $(D TypeList) of elements that
246 * satisfy the predicate.
247*/
248template Filter(alias Pred, alias List)
249{
250    static if (List.isEmpty)
251        alias TypeList!() Filter;
252    else static if (Pred!(List.head))
253        alias Cons!(List.head, Filter!(Pred, List.tail)) Filter;
254    else
255        alias Filter!(Pred, List.tail) Filter;
256}
257
258unittest {
259    static assert(is(Filter!(IsPointer, int, void*, char[], int*) == TypeTuple!(void*, int*)));
260    static assert(is(Filter!(IsPointer) == TypeTuple!()));
261}
262
263template FoldRight(alias F, alias Init, alias List)
264{
265    static if (List.isEmpty)
266        alias Init FoldRight;
267    else
268        alias F!(List.head, FoldRight!(F, Init, List.tail)) FoldRight;
269}
270
271template FoldRight(alias F, int Init, alias List)
272{
273    static if (List.isEmpty)
274        alias Init FoldRight;
275    else
276        alias F!(List.head, FoldRight!(F, Init, List.tail)) FoldRight;
277}
278
279version(unittest) {
280    template snoC(T, alias List)
281    {
282        alias TypeList!(List.toTuple, T) snoC;
283    }
284
285    template Inc(T, int i)
286    {
287        enum Inc = i + 1;
288    }
289}
290
291unittest {
292    // *** Compiler bugs
293    //static assert (snoC!(int, TypeList!(long)).toTuple == TypeTuple!(long, int));
294    //static assert (FoldRight!(snoC, TypeList!(), TypeList!(int, long)).toTuple == TypeTuple!(long, int));
295    static assert (!FoldRight!(snoC, TypeList!(), TypeList!(int)).isEmpty);
296    static assert (FoldRight!(Inc, 0, TypeList!(int, long)) == 2);
297}
298
299/** A list of functions operating on types.
300 * Used to pass multiple type functions to
301 * a template.
302 *
303 * Example:
304 * ----
305 * template Or(alias FList)
306 * {
307 *     template lambda(X)
308 *     {
309 *         static if (FList.isEmpty)
310 *             enum lambda = true;
311 *         else
312 *             enum lambda = FList.head!(X) || Or!(FList.tail).apply!(X);
313 *     }
314 *     alias lambda apply;
315 * }
316 * ----
317*/
318template TypeFunList()
319{
320    enum length = 0;
321    enum isEmpty = true;
322}
323
324template TypeFunList(alias F)
325{
326    alias F head;
327    alias TypeFunList!() tail;
328    enum length = 1;
329    enum isEmpty = false;
330}
331
332template TypeFunList(alias F, alias Tail)
333{
334    alias F head;
335    alias Tail tail;
336    enum length = 1 + Tail.length;
337    enum isEmpty = false;
338}
339
340unittest {
341    static assert (TypeFunList!().isEmpty);
342    static assert (!TypeFunList!(IsPointer).isEmpty);
343    static assert (TypeFunList!(IsPointer).tail.isEmpty);
344    static assert (TypeFunList!(IsPointer).head!(void*));
345    static assert (TypeFunList!(IsPointer, TypeFunList!(IsPointer)).head!(void *));
346    static assert (TypeFunList!(IsPointer, TypeFunList!(IsPointer)).tail.head!(void *));
347}
348
349/** Negates a type predicate.
350 * The negated predicate is a "member" $(D apply).
351 *
352 * Example:
353 * ----
354 * static assert (Not!(IsPointer).apply!(int));
355 * ----
356*/
357template Not(alias F)
358{
359    template lambda(X)
360    {
361        enum lambda = !F!(X);
362    }
363    alias lambda apply;
364}
365
366unittest {
367    static assert (Not!(IsPointer).apply!(int));
368}
369
370/** Combines two type predicates using logical OR.
371 * The resulting predicate is callable through the field $(D apply)
372 *
373 * Example:
374 * ----
375 * static assert(Or!(IsPointer, Not!(IsPointer).apply).apply!(int));
376 * ----
377*/
378template Or(alias F1, alias F2)
379{
380    template lambda(X)
381    {
382        enum lambda = F1!(X) || F2!(X);
383    }
384    alias lambda apply;
385}
386
387unittest {
388    static assert(Or!(IsPointer, IsPointer).apply!(int*));
389    static assert(Or!(IsPointer, Not!(IsPointer).apply).apply!(int));
390}
391
392/** Combines a list of type predicates using logical OR.
393 * The resulting predicate is callable through the field $(D apply)
394*/
395template Or(alias FList)
396{
397    template lambda(X)
398    {
399        static if (FList.isEmpty)
400            enum lambda = true;
401        else
402            enum lambda = FList.head!(X) || Or!(FList.tail).apply!(X);
403    }
404    alias lambda apply;
405}
406
407unittest {
408    static assert (Or!(
409        TypeFunList!(IsPointer,
410                     TypeFunList!(Not!(IsPointer).apply)
411                     )).apply!(int*));
412}
413
414/** Combines two type predicates using logical AND.
415 * The resulting predicate is callable through the field $(D apply)
416 *
417 * Example:
418 * ----
419 * static assert(!And!(IsPointer, Not!(IsPointer).apply).apply!(int));
420 * ----
421*/
422template And(alias F1, alias F2)
423{
424    template lambda(X)
425    {
426        enum lambda = F1!(X) && F2!(X);
427    }
428    alias lambda apply;
429}
430
431unittest {
432    static assert(And!(IsPointer, IsPointer).apply!(int*));
433    static assert(!And!(IsPointer, Not!(IsPointer).apply).apply!(int));
434}
435
436/** Combines a list of type predicates using logical AND.
437 * The resulting predicate is callable through the field $(D apply)
438*/
439template And(alias FList)
440{
441    template lambda(X)
442    {
443        static if (FList.isEmpty)
444            enum lambda = true;
445        else
446            enum lambda = FList.head!(X) && And!(FList.tail).apply!(X);
447    }
448    alias lambda apply;
449}
450
451unittest {
452    static assert (!And!(
453        TypeFunList!(IsPointer,
454                     TypeFunList!(Not!(IsPointer).apply)
455                     )).apply!(int*));
456}