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