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