simple_cxx11_metaprogramming.adoc 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  1. ////
  2. Copyright 2015-2017 Peter Dimov
  3. Distributed under the Boost Software License, Version 1.0.
  4. See accompanying file LICENSE_1_0.txt or copy at
  5. http://www.boost.org/LICENSE_1_0.txt
  6. ////
  7. # Simple {cpp}11 metaprogramming
  8. Peter Dimov
  9. 2015-05-26
  10. :toc: left
  11. :idprefix:
  12. :docinfo: shared-footer
  13. [.lead]
  14. __With variadic templates, parameter packs and template aliases__
  15. NOTE: I was motivated to write this after I read Eric Niebler's
  16. thought-provoking
  17. http://ericniebler.com/2014/11/13/tiny-metaprogramming-library/[Tiny
  18. Metaprogramming Library] article. Thanks Eric.
  19. ## {cpp}11 changes the playing field
  20. The wide acceptance of http://www.boost.org/libs/mpl[Boost.MPL] made {cpp}
  21. metaprogramming seem a solved problem. Perhaps MPL wasn't ideal, but it was
  22. good enough to the point that there wasn't really a need to seek or produce
  23. alternatives.
  24. {cpp}11 changed the playing field. The addition of variadic templates with
  25. their associated parameter packs added a compile-time list of types structure
  26. directly into the language. Whereas before every metaprogramming library
  27. defined its own type list, and MPL defined several, in {cpp}11, type lists are
  28. as easy as
  29. ```
  30. // C++11
  31. template<class... T> struct type_list {};
  32. ```
  33. and there is hardly a reason to use anything else.
  34. Template aliases are another game changer. Previously, "metafunctions", that
  35. is, templates that took one type and produced another, looked like
  36. ```
  37. // C++03
  38. template<class T> struct add_pointer { typedef T* type; };
  39. ```
  40. and were used in the following manner:
  41. ```
  42. // C++03
  43. typedef typename add_pointer<X>::type Xp;
  44. ```
  45. In {cpp}11, metafunctions can be template aliases, instead of class templates:
  46. ```
  47. // C++11
  48. template<class T> using add_pointer = T*;
  49. ```
  50. The above example use then becomes
  51. ```
  52. // C++11
  53. typedef add_pointer<X> Xp;
  54. ```
  55. or, if you prefer to be seen as {cpp}11-savvy,
  56. ```
  57. // C++11
  58. using Xp = add_pointer<X>;
  59. ```
  60. This is a considerable improvement in more complex expressions:
  61. ```
  62. // C++03
  63. typedef
  64. typename add_reference<
  65. typename add_const<
  66. typename add_pointer<X>::type
  67. >::type
  68. >::type Xpcr;
  69. ```
  70. ```
  71. // C++11
  72. using Xpcr = add_reference<add_const<add_pointer<X>>>;
  73. ```
  74. (The example also takes advantage of another {cpp}11 feature - you can now use
  75. `>>` to close templates without it being interpreted as a right shift.)
  76. In addition, template aliases can be passed to template template parameters:
  77. ```
  78. // C++11
  79. template<template<class... T> class F> struct X
  80. {
  81. };
  82. X<add_pointer>; // works!
  83. ```
  84. These language improvements allow for {cpp}11 metaprogramming that is
  85. substantially different than its idiomatic {cpp}03 equivalent. Boost.MPL is no
  86. longer good enough, and __something must be done__. But what?
  87. ## Type lists and mp_rename
  88. Let's start with the basics. Our basic data structure will be the type list:
  89. ```
  90. template<class... T> struct mp_list {};
  91. ```
  92. Why the `mp_` prefix? mp obviously stands for metaprogramming, but could we not
  93. have used a namespace?
  94. Indeed we could have. Past experience with Boost.MPL however indicates that
  95. name conflicts between our metaprogramming primitives and standard identifiers
  96. (such as `list`) and keywords (such as `if`, `int` or `true`) will be common
  97. and will be a source of problems. With a prefix, we avoid all that trouble.
  98. So we have our type list and can put things into it:
  99. ```
  100. using list = mp_list<int, char, float, double, void>;
  101. ```
  102. but can't do anything else with it yet. We'll need a library of primitives that
  103. operate on ``mp_list``s. But before we get into that, let's consider another
  104. interesting question first.
  105. Suppose we have our library of primitives that can do things with a `mp_list`,
  106. but some other code hands us a type list that is not an `mp_list`, such as for
  107. example an `std::tuple<int, float, void*>`, or
  108. ``http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4115.html[std::packer]<int,
  109. float, void*>``.
  110. Suppose we need to modify this external list of types in some manner (change
  111. the types into pointers, perhaps) and give back the transformed result in the
  112. form it was given to us, `std::tuple<int*, float*, void$$**$$>` in the first
  113. case and `std::packer<int*, float*, void$$**$$>` in the second.
  114. To do that, we need to first convert `std::tuple<int, float, void*>` to
  115. `mp_list<int, float, void*>`, apply `add_pointer` to each element obtaining
  116. `mp_list<int*, float*, void$$**$$>`, then convert that back to `std::tuple`.
  117. These conversion steps are a quite common occurrence, and we'll write a
  118. primitive that helps us perform them, called `mp_rename`. We want
  119. ```
  120. mp_rename<std::tuple<int, float, void*>, mp_list>
  121. ```
  122. to give us
  123. ```
  124. mp_list<int, float, void*>
  125. ```
  126. and conversely,
  127. ```
  128. mp_rename<mp_list<int, float, void*>, std::tuple>
  129. ```
  130. to give us
  131. ```
  132. std::tuple<int, float, void*>
  133. ```
  134. Here is the implementation of `mp_rename`:
  135. ```
  136. template<class A, template<class...> class B> struct mp_rename_impl;
  137. template<template<class...> class A, class... T, template<class...> class B>
  138. struct mp_rename_impl<A<T...>, B>
  139. {
  140. using type = B<T...>;
  141. };
  142. template<class A, template<class...> class B>
  143. using mp_rename = typename mp_rename_impl<A, B>::type;
  144. ```
  145. (This pattern of a template alias forwarding to a class template doing the
  146. actual work is common; class templates can be specialized, whereas template
  147. aliases cannot.)
  148. Note that `mp_rename` does not treat any list type as special, not even
  149. `mp_list`; it can rename any variadic class template into any other. You could
  150. use it to rename `std::packer` to `std::tuple` to `std::variant` (once there is
  151. such a thing) and it will happily oblige.
  152. In fact, it can even rename non-variadic class templates, as in the following
  153. examples:
  154. ```
  155. mp_rename<std::pair<int, float>, std::tuple> // -> std::tuple<int, float>
  156. mp_rename<mp_list<int, float>, std::pair> // -> std::pair<int, float>
  157. mp_rename<std::shared_ptr<int>, std::unique_ptr> // -> std::unique_ptr<int>
  158. ```
  159. There is a limit to the magic; `unique_ptr` can't be renamed to `shared_ptr`:
  160. ```
  161. mp_rename<std::unique_ptr<int>, std::shared_ptr> // error
  162. ```
  163. because `unique_ptr<int>` is actually `unique_ptr<int,
  164. std::default_delete<int>>` and `mp_rename` renames it to `shared_ptr<int,
  165. std::default_delete<int>>`, which doesn't compile. But it still works in many
  166. more cases than one would naively expect at first.
  167. With conversions no longer a problem, let's move on to primitives and define a
  168. simple one, `mp_size`, for practice. We want `mp_size<mp_list<T$$...$$>>` to
  169. give us the number of elements in the list, that is, the value of the
  170. expression `sizeof$$...$$(T)`.
  171. ```
  172. template<class L> struct mp_size_impl;
  173. template<class... T> struct mp_size_impl<mp_list<T...>>
  174. {
  175. using type = std::integral_constant<std::size_t, sizeof...(T)>;
  176. };
  177. template<class L> using mp_size = typename mp_size_impl<L>::type;
  178. ```
  179. This is relatively straightforward, except for the `std::integral_constant`.
  180. What is it and why do we need it?
  181. `std::integral_constant` is a standard {cpp}11 type that wraps an integral
  182. constant (that is, a compile-time constant integer value) into a type.
  183. Since metaprogramming operates on type lists, which can only hold types, it's
  184. convenient to represent compile-time constants as types. This allows us to
  185. treat lists of types and lists of values in a uniform manner. It is therefore
  186. idiomatic in metaprogramming to take and return types instead of values, and
  187. this is what we have done. If at some later point we want the actual value, we
  188. can use the expression `mp_size<L>::value` to retrieve it.
  189. We now have our `mp_size`, but you may have noticed that there's an interesting
  190. difference between `mp_size` and `mp_rename`. Whereas I made a point of
  191. `mp_rename` not treating `mp_list` as a special case, `mp_size` very much does:
  192. ```
  193. template<class... T> struct mp_size_impl<mp_list<T...>>
  194. ```
  195. Is this really necessary? Can we not use the same technique in the
  196. implementation of `mp_size` as we did in `mp_rename`?
  197. ```
  198. template<class L> struct mp_size_impl;
  199. template<template<class...> class L, class... T> struct mp_size_impl<L<T...>>
  200. {
  201. using type = std::integral_constant<std::size_t, sizeof...(T)>;
  202. };
  203. template<class L> using mp_size = typename mp_size_impl<L>::type;
  204. ```
  205. Yes, we very much can, and this improvement allows us to use `mp_size` on any
  206. other type lists, such as `std::tuple`. It turns `mp_size` into a truly generic
  207. primitive.
  208. This is nice. It is so nice that I'd argue that all our metaprogramming
  209. primitives ought to have this property. If someone hands us a type list in the
  210. form of an `std::tuple`, we should be able to operate on it directly, avoiding
  211. the conversions to and from `mp_list`.
  212. So do we no longer have any need for `mp_rename`? Not quite. Apart from the
  213. fact that sometimes we really do need to rename type lists, there is another
  214. surprising task for which `mp_rename` is useful.
  215. To illustrate it, let me introduce the primitive `mp_length`. It's similar to
  216. `mp_size`, but while `mp_size` takes a type list as an argument, `mp_length`
  217. takes a variadic parameter pack and returns its length; or, stated differently,
  218. it returns its number of arguments:
  219. ```
  220. template<class... T> using mp_length =
  221. std::integral_constant<std::size_t, sizeof...(T)>;
  222. ```
  223. How would we implement `mp_size` in terms of `mp_length`? One option is to just
  224. substitute the implementation of the latter into the former:
  225. ```
  226. template<template<class...> class L, class... T> struct mp_size_impl<L<T...>>
  227. {
  228. using type = mp_length<T...>;
  229. };
  230. ```
  231. but there is another way, much less mundane. Think about what `mp_size` does.
  232. It takes the argument
  233. [subs=+quotes]
  234. ```
  235. **mp_list**<int, void, float>
  236. ```
  237. and returns
  238. [subs=+quotes]
  239. ```
  240. **mp_length**<int, void, float>
  241. ```
  242. Do we already have a primitive that does a similar thing?
  243. (Not much of a choice, is there?)
  244. Indeed we have, and it's called `mp_rename`.
  245. ```
  246. template<class L> using mp_size = mp_rename<L, mp_length>;
  247. ```
  248. I don't know about you, but I find this technique fascinating. It exploits the
  249. structural similarity between a list, `L<T$$...$$>`, and a metafunction "call",
  250. `F<T$$...$$>`, and the fact that the language sees the things the same way and
  251. allows us to pass the template alias `mp_length` to `mp_rename` as if it were
  252. an ordinary class template such as `mp_list`.
  253. (Other metaprogramming libraries provide a dedicated `apply` primitive for
  254. this job. `apply<F, L>` calls the metafunction `F` with the contents of the
  255. list `L`. We'll add an alias `mp_apply<F, L>` that calls `mp_rename<L, F>` for
  256. readability.)
  257. ```
  258. template<template<class...> class F, class L> using mp_apply = mp_rename<L, F>;
  259. ```
  260. ## mp_transform
  261. Let's revisit the example I gave earlier - someone hands us `std::tuple<X, Y,
  262. Z>` and we need to compute `std::tuple<X*, Y*, Z*>`. We already have
  263. `add_pointer`:
  264. ```
  265. template<class T> using add_pointer = T*;
  266. ```
  267. so we just need to apply it to each element of the input tuple.
  268. The algorithm that takes a function and a list and applies the function to each
  269. element is called `transform` in Boost.MPL and the STL and `map` in functional
  270. languages. We'll use `transform`, for consistency with the established {cpp}
  271. practice (`map` is a data structure in both the STL and Boost.MPL.)
  272. We'll call our algorithm `mp_transform`, and `mp_transform<F, L>` will apply
  273. `F` to each element of `L` and return the result. Usually, the argument order
  274. is reversed and the function comes last. Our reasons to put it at the front
  275. will become evident later.
  276. There are many ways to implement `mp_transform`; the one we'll pick will make
  277. use of another primitive, `mp_push_front`. `mp_push_front<L, T>`, as its name
  278. implies, adds `T` as a first element in `L`:
  279. ```
  280. template<class L, class T> struct mp_push_front_impl;
  281. template<template<class...> class L, class... U, class T>
  282. struct mp_push_front_impl<L<U...>, T>
  283. {
  284. using type = L<T, U...>;
  285. };
  286. template<class L, class T>
  287. using mp_push_front = typename mp_push_front_impl<L, T>::type;
  288. ```
  289. There is no reason to constrain `mp_push_front` to a single element though. In
  290. {cpp}11, variadic templates should be our default choice, and the
  291. implementation of `mp_push_front` that can take an arbitrary number of elements
  292. is almost identical:
  293. ```
  294. template<class L, class... T> struct mp_push_front_impl;
  295. template<template<class...> class L, class... U, class... T>
  296. struct mp_push_front_impl<L<U...>, T...>
  297. {
  298. using type = L<T..., U...>;
  299. };
  300. template<class L, class... T>
  301. using mp_push_front = typename mp_push_front_impl<L, T...>::type;
  302. ```
  303. On to `mp_transform`:
  304. ```
  305. template<template<class...> class F, class L> struct mp_transform_impl;
  306. template<template<class...> class F, class L>
  307. using mp_transform = typename mp_transform_impl<F, L>::type;
  308. template<template<class...> class F, template<class...> class L>
  309. struct mp_transform_impl<F, L<>>
  310. {
  311. using type = L<>;
  312. };
  313. template<template<class...> class F, template<class...> class L, class T1, class... T>
  314. struct mp_transform_impl<F, L<T1, T...>>
  315. {
  316. using _first = F<T1>;
  317. using _rest = mp_transform<F, L<T...>>;
  318. using type = mp_push_front<_rest, _first>;
  319. };
  320. ```
  321. This is a straightforward recursive implementation that should be familiar to
  322. people with functional programming background.
  323. Can we do better? It turns out that in {cpp}11, we can.
  324. ```
  325. template<template<class...> class F, class L> struct mp_transform_impl;
  326. template<template<class...> class F, class L>
  327. using mp_transform = typename mp_transform_impl<F, L>::type;
  328. template<template<class...> class F, template<class...> class L, class... T>
  329. struct mp_transform_impl<F, L<T...>>
  330. {
  331. using type = L<F<T>...>;
  332. };
  333. ```
  334. Here we take advantage of the fact that pack expansion is built into the
  335. language, so the `F<T>$$...$$` part does all the iteration work for us.
  336. We can now solve our original challenge: given an `std::tuple` of types, return
  337. an `std::tuple` of pointers to these types:
  338. ```
  339. using input = std::tuple<int, void, float>;
  340. using expected = std::tuple<int*, void*, float*>;
  341. using result = mp_transform<add_pointer, input>;
  342. static_assert( std::is_same<result, expected>::value, "" );
  343. ```
  344. ## mp_transform, part two
  345. What if we had a pair of tuples as input, and had to produce the corresponding
  346. tuple of pairs? For example, given
  347. ```
  348. using input = std::pair<std::tuple<X1, X2, X3>, std::tuple<Y1, Y2, Y3>>;
  349. ```
  350. we had to produce
  351. ```
  352. using expected = std::tuple<std::pair<X1, Y1>, std::pair<X2, Y2>, std::pair<X3, Y3>>;
  353. ```
  354. We need to take the two lists, represented by tuples in the input, and combine
  355. them pairwise by using `std::pair`. If we think of `std::pair` as a function
  356. `F`, this task appears very similar to `mp_transform`, except we need to use a
  357. binary function and two lists.
  358. Changing our unary transform algorithm into a binary one isn't hard:
  359. ```
  360. template<template<class...> class F, class L1, class L2>
  361. struct mp_transform2_impl;
  362. template<template<class...> class F, class L1, class L2>
  363. using mp_transform2 = typename mp_transform2_impl<F, L1, L2>::type;
  364. template<template<class...> class F,
  365. template<class...> class L1, class... T1,
  366. template<class...> class L2, class... T2>
  367. struct mp_transform2_impl<F, L1<T1...>, L2<T2...>>
  368. {
  369. static_assert( sizeof...(T1) == sizeof...(T2),
  370. "The arguments of mp_transform2 should be of the same size" );
  371. using type = L1<F<T1,T2>...>;
  372. };
  373. ```
  374. and we can now do
  375. ```
  376. using input = std::pair<std::tuple<X1, X2, X3>, std::tuple<Y1, Y2, Y3>>;
  377. using expected = std::tuple<std::pair<X1, Y1>, std::pair<X2, Y2>, std::pair<X3, Y3>>;
  378. using result = mp_transform2<std::pair, input::first_type, input::second_type>;
  379. static_assert( std::is_same<result, expected>::value, "" );
  380. ```
  381. again exploiting the similarity between metafunctions and ordinary class
  382. templates such as `std::pair`, this time in the other direction; we pass
  383. `std::pair` where `mp_transform2` expects a metafunction.
  384. Do we _have_ to use separate transform algorithms for each arity though? If we
  385. need a transform algorithm that takes a ternary function and three lists,
  386. should we name it `mp_transform3`? No, this is exactly why we put the function
  387. first. We just have to change `mp_transform` to be variadic:
  388. ```
  389. template<template<class...> class F, class... L> struct mp_transform_impl;
  390. template<template<class...> class F, class... L>
  391. using mp_transform = typename mp_transform_impl<F, L...>::type;
  392. ```
  393. and then add the unary and binary specializations:
  394. ```
  395. template<template<class...> class F, template<class...> class L, class... T>
  396. struct mp_transform_impl<F, L<T...>>
  397. {
  398. using type = L<F<T>...>;
  399. };
  400. template<template<class...> class F,
  401. template<class...> class L1, class... T1,
  402. template<class...> class L2, class... T2>
  403. struct mp_transform_impl<F, L1<T1...>, L2<T2...>>
  404. {
  405. static_assert( sizeof...(T1) == sizeof...(T2),
  406. "The arguments of mp_transform should be of the same size" );
  407. using type = L1<F<T1,T2>...>;
  408. };
  409. ```
  410. We can also add ternary and further specializations.
  411. Is it possible to implement the truly variadic `mp_transform`, one that works
  412. with an arbitrary number of lists? It is in principle, and I'll show one
  413. possible abridged implementation here for completeness:
  414. ```
  415. template<template<class...> class F, class E, class... L>
  416. struct mp_transform_impl;
  417. template<template<class...> class F, class... L>
  418. using mp_transform = typename mp_transform_impl<F, mp_empty<L...>, L...>::type;
  419. template<template<class...> class F, class L1, class... L>
  420. struct mp_transform_impl<F, mp_true, L1, L...>
  421. {
  422. using type = mp_clear<L1>;
  423. };
  424. template<template<class...> class F, class... L>
  425. struct mp_transform_impl<F, mp_false, L...>
  426. {
  427. using _first = F< typename mp_front_impl<L>::type... >;
  428. using _rest = mp_transform< F, typename mp_pop_front_impl<L>::type... >;
  429. using type = mp_push_front<_rest, _first>;
  430. };
  431. ```
  432. but will omit the primitives that it uses. These are
  433. * `mp_true` -- an alias for `std::integral_constant<bool, true>`.
  434. * `mp_false` -- an alias for `std::integral_constant<bool, false>`.
  435. * `mp_empty<L$$...$$>` -- returns `mp_true` if all lists are empty, `mp_false`
  436. otherwise.
  437. * `mp_clear<L>` -- returns an empty list of the same type as `L`.
  438. * `mp_front<L>` -- returns the first element of `L`.
  439. * `mp_pop_front<L>` -- returns `L` without its first element.
  440. There is one interesting difference between the recursive `mp_transform`
  441. implementation and the language-based one. `mp_transform<add_pointer,
  442. std::pair<int, float>>` works with the `F<T>$$...$$` implementation and fails
  443. with the recursive one, because `std::pair` is not a real type list and can
  444. only hold exactly two types.
  445. ## The infamous tuple_cat challenge
  446. Eric Niebler, in his
  447. http://ericniebler.com/2014/11/13/tiny-metaprogramming-library/[Tiny
  448. Metaprogramming Library] article, gives the function
  449. http://en.cppreference.com/w/cpp/utility/tuple/tuple_cat[`std::tuple_cat`] as a
  450. kind of a metaprogramming challenge. `tuple_cat` is a variadic template
  451. function that takes a number of tuples and concatenates them into another
  452. `std::tuple`. This is Eric's solution:
  453. ```
  454. namespace detail
  455. {
  456. template<typename Ret, typename...Is, typename ...Ks,
  457. typename Tuples>
  458. Ret tuple_cat_(typelist<Is...>, typelist<Ks...>,
  459. Tuples tpls)
  460. {
  461. return Ret{std::get<Ks::value>(
  462. std::get<Is::value>(tpls))...};
  463. }
  464. }
  465. template<typename...Tuples,
  466. typename Res =
  467. typelist_apply_t<
  468. meta_quote<std::tuple>,
  469. typelist_cat_t<typelist<as_typelist_t<Tuples>...> > > >
  470. Res tuple_cat(Tuples &&... tpls)
  471. {
  472. static constexpr std::size_t N = sizeof...(Tuples);
  473. // E.g. [0,0,0,2,2,2,3,3]
  474. using inner =
  475. typelist_cat_t<
  476. typelist_transform_t<
  477. typelist<as_typelist_t<Tuples>...>,
  478. typelist_transform_t<
  479. as_typelist_t<make_index_sequence<N> >,
  480. meta_quote<meta_always> >,
  481. meta_quote<typelist_transform_t> > >;
  482. // E.g. [0,1,2,0,1,2,0,1]
  483. using outer =
  484. typelist_cat_t<
  485. typelist_transform_t<
  486. typelist<as_typelist_t<Tuples>...>,
  487. meta_compose<
  488. meta_quote<as_typelist_t>,
  489. meta_quote_i<std::size_t, make_index_sequence>,
  490. meta_quote<typelist_size_t> > > >;
  491. return detail::tuple_cat_<Res>(
  492. inner{},
  493. outer{},
  494. std::forward_as_tuple(std::forward<Tuples>(tpls)...));
  495. }
  496. ```
  497. All right, challenge accepted. Let's see what we can do.
  498. As Eric explains, this implementation relies on the clever trick of packing the
  499. input tuples into a tuple, creating two arrays of indices, `inner` and `outer`,
  500. then indexing the outer tuple with the outer indices and the result, which is
  501. one of our input tuples, with the inner indices.
  502. So, for example, if tuple_cat is invoked as
  503. ```
  504. std::tuple<int, short, long> t1;
  505. std::tuple<> t2;
  506. std::tuple<float, double, long double> t3;
  507. std::tuple<void*, char*> t4;
  508. auto res = tuple_cat(t1, t2, t3, t4);
  509. ```
  510. we'll create the tuple
  511. ```
  512. std::tuple<std::tuple<int, short, long>, std::tuple<>,
  513. std::tuple<float, double, long double>, std::tuple<void*, char*>> t{t1, t2, t3, t4};
  514. ```
  515. and then extract the elements of t via
  516. ```
  517. std::get<0>(std::get<0>(t)), // t1[0]
  518. std::get<1>(std::get<0>(t)), // t1[1]
  519. std::get<2>(std::get<0>(t)), // t1[2]
  520. std::get<0>(std::get<2>(t)), // t3[0]
  521. std::get<1>(std::get<2>(t)), // t3[1]
  522. std::get<2>(std::get<2>(t)), // t3[2]
  523. std::get<0>(std::get<3>(t)), // t4[0]
  524. std::get<1>(std::get<3>(t)), // t4[1]
  525. ```
  526. (`t2` is empty, so we take nothing from it.)
  527. The first column of integers is the `outer` array, the second one - the `inner`
  528. array, and these are what we need to compute. But first, let's deal with the
  529. return type of `tuple_cat`.
  530. The return type of `tuple_cat` is just the concatenation of the arguments,
  531. viewed as type lists. The metaprogramming algorithm that concatenates lists is
  532. called
  533. https://ericniebler.github.io/meta/group__transformation.html[`meta::concat`]
  534. in Eric Niebler's https://github.com/ericniebler/meta[Meta] library, but I'll
  535. call it `mp_append`, after its classic Lisp name.
  536. (Lisp is today's equivalent of Latin. Educated people are supposed to have
  537. studied and forgotten it.)
  538. ```
  539. template<class... L> struct mp_append_impl;
  540. template<class... L> using mp_append = typename mp_append_impl<L...>::type;
  541. template<> struct mp_append_impl<>
  542. {
  543. using type = mp_list<>;
  544. };
  545. template<template<class...> class L, class... T> struct mp_append_impl<L<T...>>
  546. {
  547. using type = L<T...>;
  548. };
  549. template<template<class...> class L1, class... T1,
  550. template<class...> class L2, class... T2, class... Lr>
  551. struct mp_append_impl<L1<T1...>, L2<T2...>, Lr...>
  552. {
  553. using type = mp_append<L1<T1..., T2...>, Lr...>;
  554. };
  555. ```
  556. That was fairly easy. There are other ways to implement `mp_append`, but this
  557. one demonstrates how the language does most of the work for us via pack
  558. expansion. This is a common theme in {cpp}11.
  559. Note how `mp_append` returns the same list type as its first argument. Of
  560. course, in the case in which no arguments are given, there is no first argument
  561. from which to take the type, so I've arbitrarily chosen to return an empty
  562. `mp_list`.
  563. We're now ready with the declaration of `tuple_cat`:
  564. ```
  565. template<class... Tp,
  566. class R = mp_append<typename std::remove_reference<Tp>::type...>>
  567. R tuple_cat( Tp &&... tp );
  568. ```
  569. The reason we need `remove_reference` is because of the rvalue reference
  570. parameters, used to implement perfect forwarding. If the argument is an lvalue,
  571. such as for example `t1` above, its corresponding type will be a reference to a
  572. tuple -- `std::tuple<int, short, long>&` in ``t1``'s case. Our primitives do
  573. not recognize references to tuples as type lists, so we need to strip them off.
  574. There are two problems with our return type computation though. One, what if
  575. `tuple_cat` is called without any arguments? We return `mp_list<>` in that
  576. case, but the correct result is `std::tuple<>`.
  577. Two, what if we call `tuple_cat` with a first argument that is a `std::pair`?
  578. We'll try to append more elements to `std::pair`, and it will fail.
  579. We can solve both our problems by using an empty tuple as the first argument to
  580. `mp_append`:
  581. ```
  582. template<class... Tp,
  583. class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
  584. R tuple_cat( Tp &&... tp );
  585. ```
  586. With the return type taken care of, let's now move on to computing inner. We
  587. have
  588. ```
  589. [x1, x2, x3], [], [y1, y2, y3], [z1, z2]
  590. ```
  591. as input and we need to output
  592. ```
  593. [0, 0, 0, 2, 2, 2, 3, 3]
  594. ```
  595. which is the concatenation of
  596. ```
  597. [0, 0, 0], [], [2, 2, 2], [3, 3]
  598. ```
  599. Here each tuple is the same size as the input, but is filled with a constant
  600. that represents its index in the argument list. The first tuple is filled with
  601. 0, the second with 1, the third with 2, and so on.
  602. We can achieve this result if we first compute a list of indices, in our case
  603. `[0, 1, 2, 3]`, then use binary `mp_transform` on the two lists
  604. ```
  605. [[x1, x2, x3], [], [y1, y2, y3], [z1, z2]]
  606. [0, 1, 2, 3]
  607. ```
  608. and a function which takes a list and an integer (in the form of an
  609. `std::integral_constant`) and returns a list that is the same size as the
  610. original, but filled with the second argument.
  611. We'll call this function `mp_fill`, after `std::fill`.
  612. Functional programmers will immediately realize that `mp_fill` is
  613. `mp_transform` with a function that returns a constant, and here's an
  614. implementation along these lines:
  615. ```
  616. template<class V> struct mp_constant
  617. {
  618. template<class...> using apply = V;
  619. };
  620. template<class L, class V>
  621. using mp_fill = mp_transform<mp_constant<V>::template apply, L>;
  622. ```
  623. Here's an alternate implementation:
  624. ```
  625. template<class L, class V> struct mp_fill_impl;
  626. template<template<class...> class L, class... T, class V>
  627. struct mp_fill_impl<L<T...>, V>
  628. {
  629. template<class...> using _fv = V;
  630. using type = L<_fv<T>...>;
  631. };
  632. template<class L, class V> using mp_fill = typename mp_fill_impl<L, V>::type;
  633. ```
  634. These demonstrate different styles and choosing one over the other is largely a
  635. matter of taste here. In the first case, we combine existing primitives; in the
  636. second case, we "inline" `mp_const` and even `mp_transform` in the body of
  637. `mp_fill_impl`.
  638. Most {cpp}11 programmers will probably find the second implementation easier to
  639. read.
  640. We can now `mp_fill`, but we still need the `[0, 1, 2, 3]` index sequence. We
  641. could write an algorithm `mp_iota` for that (named after
  642. http://en.cppreference.com/w/cpp/algorithm/iota[`std::iota`]), but it so
  643. happens that {cpp}14 already has a standard way of generating an index
  644. sequence, called
  645. http://en.cppreference.com/w/cpp/utility/integer_sequence[`std::make_index_sequence`].
  646. Since Eric's original solution makes use of `make_index_sequence`, let's follow
  647. his lead.
  648. Technically, this takes us outside of {cpp}11, but `make_index_sequence` is not
  649. hard to implement (if efficiency is not a concern):
  650. ```
  651. template<class T, T... Ints> struct integer_sequence
  652. {
  653. };
  654. template<class S> struct next_integer_sequence;
  655. template<class T, T... Ints> struct next_integer_sequence<integer_sequence<T, Ints...>>
  656. {
  657. using type = integer_sequence<T, Ints..., sizeof...(Ints)>;
  658. };
  659. template<class T, T I, T N> struct make_int_seq_impl;
  660. template<class T, T N>
  661. using make_integer_sequence = typename make_int_seq_impl<T, 0, N>::type;
  662. template<class T, T I, T N> struct make_int_seq_impl
  663. {
  664. using type = typename next_integer_sequence<
  665. typename make_int_seq_impl<T, I+1, N>::type>::type;
  666. };
  667. template<class T, T N> struct make_int_seq_impl<T, N, N>
  668. {
  669. using type = integer_sequence<T>;
  670. };
  671. template<std::size_t... Ints>
  672. using index_sequence = integer_sequence<std::size_t, Ints...>;
  673. template<std::size_t N>
  674. using make_index_sequence = make_integer_sequence<std::size_t, N>;
  675. ```
  676. We can now obtain an `index_sequence<0, 1, 2, 3>`:
  677. ```
  678. template<class... Tp,
  679. class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
  680. R tuple_cat( Tp &&... tp )
  681. {
  682. std::size_t const N = sizeof...(Tp);
  683. // inner
  684. using seq = make_index_sequence<N>;
  685. }
  686. ```
  687. but `make_index_sequence<4>` returns `integer_sequence<std::size_t, 0, 1, 2,
  688. 3>`, which is not a type list. In order to work with it, we need to convert it
  689. to a type list, so we'll introduce a function `mp_from_sequence` that does
  690. that.
  691. ```
  692. template<class S> struct mp_from_sequence_impl;
  693. template<template<class T, T... I> class S, class U, U... J>
  694. struct mp_from_sequence_impl<S<U, J...>>
  695. {
  696. using type = mp_list<std::integral_constant<U, J>...>;
  697. };
  698. template<class S> using mp_from_sequence = typename mp_from_sequence_impl<S>::type;
  699. ```
  700. We can now compute the two lists that we wanted to transform with `mp_fill`:
  701. ```
  702. template<class... Tp,
  703. class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
  704. R tuple_cat( Tp &&... tp )
  705. {
  706. std::size_t const N = sizeof...(Tp);
  707. // inner
  708. using list1 = mp_list<typename std::remove_reference<Tp>::type...>;
  709. using list2 = mp_from_sequence<make_index_sequence<N>>;
  710. // list1: [[x1, x2, x3], [], [y1, y2, y3], [z1, z2]]
  711. // list2: [0, 1, 2, 3]
  712. return R{};
  713. }
  714. ```
  715. and finish the job of computing `inner`:
  716. ```
  717. template<class... Tp,
  718. class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
  719. R tuple_cat( Tp &&... tp )
  720. {
  721. std::size_t const N = sizeof...(Tp);
  722. // inner
  723. using list1 = mp_list<typename std::remove_reference<Tp>::type...>;
  724. using list2 = mp_from_sequence<make_index_sequence<N>>;
  725. // list1: [[x1, x2, x3], [], [y1, y2, y3], [z1, z2]]
  726. // list2: [0, 1, 2, 3]
  727. using list3 = mp_transform<mp_fill, list1, list2>;
  728. // list3: [[0, 0, 0], [], [2, 2, 2], [3, 3]]
  729. using inner = mp_rename<list3, mp_append>; // or mp_apply<mp_append, list3>
  730. // inner: [0, 0, 0, 2, 2, 2, 3, 3]
  731. return R{};
  732. }
  733. ```
  734. For `outer`, we again have
  735. ```
  736. [x1, x2, x3], [], [y1, y2, y3], [z1, z2]
  737. ```
  738. as input and we need to output
  739. ```
  740. [0, 1, 2, 0, 1, 2, 0, 1]
  741. ```
  742. which is the concatenation of
  743. ```
  744. [0, 1, 2], [], [0, 1, 2], [0, 1]
  745. ```
  746. The difference here is that instead of filling the tuple with a constant value,
  747. we need to fill it with increasing values, starting from 0, that is, with the
  748. result of `make_index_sequence<N>`, where `N` is the number of elements.
  749. The straightforward way to do that is to just define a metafunction `F` that
  750. does what we want, then use `mp_transform` to apply it to the input:
  751. ```
  752. template<class N> using mp_iota = mp_from_sequence<make_index_sequence<N::value>>;
  753. template<class L> using F = mp_iota<mp_size<L>>;
  754. template<class... Tp,
  755. class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
  756. R tuple_cat( Tp &&... tp )
  757. {
  758. std::size_t const N = sizeof...(Tp);
  759. // outer
  760. using list1 = mp_list<typename std::remove_reference<Tp>::type...>;
  761. using list2 = mp_transform<F, list1>;
  762. // list2: [[0, 1, 2], [], [0, 1, 2], [0, 1]]
  763. using outer = mp_rename<list2, mp_append>;
  764. // outer: [0, 1, 2, 0, 1, 2, 0, 1]
  765. return R{};
  766. }
  767. ```
  768. Well that was easy. Surprisingly easy. The one small annoyance is that we can't
  769. define `F` inside `tuple_cat` - templates can't be defined in functions.
  770. Let's put everything together.
  771. ```
  772. template<class N> using mp_iota = mp_from_sequence<make_index_sequence<N::value>>;
  773. template<class L> using F = mp_iota<mp_size<L>>;
  774. template<class R, class...Is, class... Ks, class Tp>
  775. R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
  776. {
  777. return R{ std::get<Ks::value>(std::get<Is::value>(tp))... };
  778. }
  779. template<class... Tp,
  780. class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
  781. R tuple_cat( Tp &&... tp )
  782. {
  783. std::size_t const N = sizeof...(Tp);
  784. // inner
  785. using list1 = mp_list<typename std::remove_reference<Tp>::type...>;
  786. using list2 = mp_from_sequence<make_index_sequence<N>>;
  787. // list1: [[x1, x2, x3], [], [y1, y2, y3], [z1, z2]]
  788. // list2: [0, 1, 2, 3]
  789. using list3 = mp_transform<mp_fill, list1, list2>;
  790. // list3: [[0, 0, 0], [], [2, 2, 2], [3, 3]]
  791. using inner = mp_rename<list3, mp_append>; // or mp_apply<mp_append, list3>
  792. // inner: [0, 0, 0, 2, 2, 2, 3, 3]
  793. // outer
  794. using list4 = mp_transform<F, list1>;
  795. // list4: [[0, 1, 2], [], [0, 1, 2], [0, 1]]
  796. using outer = mp_rename<list4, mp_append>;
  797. // outer: [0, 1, 2, 0, 1, 2, 0, 1]
  798. return tuple_cat_<R>( inner(), outer(),
  799. std::forward_as_tuple( std::forward<Tp>(tp)... ) );
  800. }
  801. ```
  802. This almost compiles, except that our `inner` happens to be a `std::tuple`, but
  803. our helper function expects an `mp_list`. (`outer` is already an `mp_list`, by
  804. sheer luck.) We can fix that easily enough.
  805. ```
  806. return tuple_cat_<R>( mp_rename<inner, mp_list>(), outer(),
  807. std::forward_as_tuple( std::forward<Tp>(tp)... ) );
  808. ```
  809. Let's define a `print_tuple` function and see if everything checks out.
  810. ```
  811. template<int I, int N, class... T> struct print_tuple_
  812. {
  813. void operator()( std::tuple<T...> const & tp ) const
  814. {
  815. using Tp = typename std::tuple_element<I, std::tuple<T...>>::type;
  816. print_type<Tp>( " ", ": " );
  817. std::cout << std::get<I>( tp ) << ";";
  818. print_tuple_< I+1, N, T... >()( tp );
  819. }
  820. };
  821. template<int N, class... T> struct print_tuple_<N, N, T...>
  822. {
  823. void operator()( std::tuple<T...> const & ) const
  824. {
  825. }
  826. };
  827. template<class... T> void print_tuple( std::tuple<T...> const & tp )
  828. {
  829. std::cout << "{";
  830. print_tuple_<0, sizeof...(T), T...>()( tp );
  831. std::cout << " }\n";
  832. }
  833. int main()
  834. {
  835. std::tuple<int, long> t1{ 1, 2 };
  836. std::tuple<> t2;
  837. std::tuple<float, double, long double> t3{ 3, 4, 5 };
  838. std::pair<void const*, char const*> t4{ "pv", "test" };
  839. using expected = std::tuple<int, long, float, double, long double,
  840. void const*, char const*>;
  841. auto result = ::tuple_cat( t1, t2, t3, t4 );
  842. static_assert( std::is_same<decltype(result), expected>::value, "" );
  843. print_tuple( result );
  844. }
  845. ```
  846. Output:
  847. ```
  848. { int: 1; long: 2; float: 3; double: 4; long double: 5; void const*: 0x407086;
  849. char const*: test; }
  850. ```
  851. Seems to work. But there's at least one error left. To see why, replace the
  852. first tuple
  853. ```
  854. std::tuple<int, long> t1{ 1, 2 };
  855. ```
  856. with a pair:
  857. ```
  858. std::pair<int, long> t1{ 1, 2 };
  859. ```
  860. We now get an error at
  861. ```
  862. using inner = mp_rename<list3, mp_append>;
  863. ```
  864. because the first element of `list3` is an `std::pair`, which `mp_append` tries
  865. and fails to use as its return type.
  866. There are two ways to fix that. The first one is to apply the same trick we
  867. used for the return type, and insert an empty `mp_list` at the front of
  868. `list3`, which `mp_append` will use as a return type:
  869. ```
  870. using inner = mp_rename<mp_push_front<list3, mp_list<>>, mp_append>;
  871. ```
  872. The second way is to just convert all inputs to mp_list:
  873. ```
  874. using list1 = mp_list<
  875. mp_rename<typename std::remove_reference<Tp>::type, mp_list>...>;
  876. ```
  877. In both cases, inner will now be an `mp_list`, so we can omit the `mp_rename`
  878. in the call to `tuple_cat_`.
  879. We're done. The results hopefully speak for themselves.
  880. ## Higher order metaprogramming, or lack thereof
  881. Perhaps by now you're wondering why this article is called "Simple {cpp}11
  882. metaprogramming", since what we covered so far wasn't particularly simple.
  883. The _relative_ simplicity of our approach stems from the fact that we've not
  884. been doing any higher order metaprogramming, that is, we haven't introduced any
  885. primitives that return metafunctions, such as `compose`, `bind`, or a lambda
  886. library.
  887. I posit that such higher order metaprogramming is, in the majority of cases,
  888. not necessary in {cpp}11. Consider, for example, Eric Niebler's solution given
  889. above:
  890. ```
  891. using outer =
  892. typelist_cat_t<
  893. typelist_transform_t<
  894. typelist<as_typelist_t<Tuples>...>,
  895. meta_compose<
  896. meta_quote<as_typelist_t>,
  897. meta_quote_i<std::size_t, make_index_sequence>,
  898. meta_quote<typelist_size_t> > > >;
  899. ```
  900. The `meta_compose` expression takes three other ("quoted") metafunctions and
  901. creates a new metafunction that applies them in order. Eric uses this example
  902. as motivation to introduce the concept of a "metafunction class" and then to
  903. supply various primitives that operate on metafunction classes.
  904. But when we have metafunctions `F`, `G` and `H`, instead of using
  905. `meta_compose`, in {cpp}11 we can just do
  906. ```
  907. template<class... T> using Fgh = F<G<H<T...>>>;
  908. ```
  909. and that's it. The language makes defining composite functions easy, and there
  910. is no need for library support. If the functions to be composed are
  911. `as_typelist_t`, `std::make_index_sequence` and `typelist_size_t`, we just
  912. define
  913. ```
  914. template<class... T>
  915. using F = as_typelist_t<std::make_index_sequence<typelist_size_t<T...>::value>>;
  916. ```
  917. Similarly, if we need a metafunction that will return `sizeof(T) < sizeof(U)`,
  918. there is no need to enlist a metaprogramming lambda library as in
  919. ```
  920. lambda<_a, _b, less<sizeof_<_a>, sizeof_<_b>>>>
  921. ```
  922. We could just define it inline:
  923. ```
  924. template<class T, class U> using sizeof_less = mp_bool<(sizeof(T) < sizeof(U))>;
  925. ```
  926. ## One more thing
  927. Finally, let me show the implementations of `mp_count` and `mp_count_if`, for
  928. no reason other than I find them interesting. `mp_count<L, V>` returns the
  929. number of occurrences of the type `V` in the list `L`; `mp_count_if<L, P>`
  930. counts the number of types in `L` for which `P<T>` is `true`.
  931. As a first step, I'll implement `mp_plus`. `mp_plus` is a variadic (not just
  932. binary) metafunction that returns the sum of its arguments.
  933. ```
  934. template<class... T> struct mp_plus_impl;
  935. template<class... T> using mp_plus = typename mp_plus_impl<T...>::type;
  936. template<> struct mp_plus_impl<>
  937. {
  938. using type = std::integral_constant<int, 0>;
  939. };
  940. template<class T1, class... T> struct mp_plus_impl<T1, T...>
  941. {
  942. static constexpr auto _v = T1::value + mp_plus<T...>::value;
  943. using type = std::integral_constant<
  944. typename std::remove_const<decltype(_v)>::type, _v>;
  945. };
  946. ```
  947. Now that we have `mp_plus`, `mp_count` is just
  948. ```
  949. template<class L, class V> struct mp_count_impl;
  950. template<template<class...> class L, class... T, class V>
  951. struct mp_count_impl<L<T...>, V>
  952. {
  953. using type = mp_plus<std::is_same<T, V>...>;
  954. };
  955. template<class L, class V> using mp_count = typename mp_count_impl<L, V>::type;
  956. ```
  957. This is another illustration of the power of parameter pack expansion. It's a
  958. pity that we can't use pack expansion in `mp_plus` as well, to obtain
  959. ```
  960. T1::value + T2::value + T3::value + T4::value + ...
  961. ```
  962. directly. It would have been nice for `T::value + $$...$$` to have been
  963. supported, and it appears that in {cpp}17, it will be.
  964. `mp_count_if` is similarly straightforward:
  965. ```
  966. template<class L, template<class...> class P> struct mp_count_if_impl;
  967. template<template<class...> class L, class... T, template<class...> class P>
  968. struct mp_count_if_impl<L<T...>, P>
  969. {
  970. using type = mp_plus<P<T>...>;
  971. };
  972. template<class L, template<class...> class P>
  973. using mp_count_if = typename mp_count_if_impl<L, P>::type;
  974. ```
  975. at least if we require `P` to return `bool`. If not, we'll have to coerce
  976. `P<T>::value` to 0 or 1, or the count will not be correct.
  977. ```
  978. template<bool v> using mp_bool = std::integral_constant<bool, v>;
  979. template<class L, template<class...> class P> struct mp_count_if_impl;
  980. template<template<class...> class L, class... T, template<class...> class P>
  981. struct mp_count_if_impl<L<T...>, P>
  982. {
  983. using type = mp_plus<mp_bool<P<T>::value != 0>...>;
  984. };
  985. template<class L, template<class...> class P>
  986. using mp_count_if = typename mp_count_if_impl<L, P>::type;
  987. ```
  988. The last primitive I'll show is `mp_contains`. `mp_contains<L, V>` returns
  989. whether the list `L` contains the type `V`:
  990. ```
  991. template<class L, class V> using mp_contains = mp_bool<mp_count<L, V>::value != 0>;
  992. ```
  993. At first sight, this implementation appears horribly naive and inefficient --
  994. why would we need to count all the occurrences just to throw that away if we're
  995. only interested in a boolean result -- but it's actually pretty competitive and
  996. perfectly usable. We just need to add one slight optimization to `mp_plus`, the
  997. engine behind `mp_count` and `mp_contains`:
  998. ```
  999. template<class T1, class T2, class T3, class T4, class T5,
  1000. class T6, class T7, class T8, class T9, class T10, class... T>
  1001. struct mp_plus_impl<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T...>
  1002. {
  1003. static constexpr auto _v = T1::value + T2::value + T3::value + T4::value +
  1004. T5::value + T6::value + T7::value + T8::value + T9::value + T10::value +
  1005. mp_plus<T...>::value;
  1006. using type = std::integral_constant<
  1007. typename std::remove_const<decltype(_v)>::type, _v>;
  1008. };
  1009. ```
  1010. This cuts the number of template instantiations approximately tenfold.
  1011. ## Conclusion
  1012. I have outlined an approach to metaprogramming in {cpp}11 that
  1013. * takes advantage of variadic templates, parameter pack expansion, and template
  1014. aliases;
  1015. * operates on any variadic template `L<T$$...$$>`, treating it as its
  1016. fundamental data structure, without mandating a specific type list
  1017. representation;
  1018. * uses template aliases as its metafunctions, with the expression `F<T$$...$$>`
  1019. serving as the equivalent of a function call;
  1020. * exploits the structural similarity between the data structure `L<T$$...$$>`
  1021. and the metafunction call `F<T$$...$$>`;
  1022. * leverages parameter pack expansion as much as possible, instead of using the
  1023. traditional recursive implementations;
  1024. * relies on inline definitions of template aliases for function composition,
  1025. instead of providing library support for this task.
  1026. ## Further reading
  1027. <<simple_cxx11_metaprogramming_2.adoc#,Part 2 is now available>>, in which I
  1028. show algorithms that allow us to treat type lists as sets, maps, and vectors,
  1029. and demonstrate various {cpp}11 implementation techniques in the process.