Variadic Templates
Andrei Alexandrescu Research Scientist Facebook
c 2012 Andrei Alexandrescu, Facebook.
1 / 34
Twitter stream highlight
Dont think Ill be able to stay up to see whatever Cthuloid-template-horror Andrei Alexandrescu has in store. #GoingNative
c 2012 Andrei Alexandrescu, Facebook.
2 / 34
This talk
Motivation and fundamentals True variadic functions
std::tuple
c 2012 Andrei Alexandrescu, Facebook.
3 / 34
Motivation and fundamentals
c 2012 Andrei Alexandrescu, Facebook.
4 / 34
Motivation
Dene typesafe variadic functions C99 macros safer than C++03 variadic functions?! Forwarding with before/after hooks Dene algebraic types without contortions Sum types (variant) Product types (tuple) Specify settings and parameters in policy-based designs
c 2012 Andrei Alexandrescu, Facebook.
5 / 34
Fundamentals
template <typename... Ts> class C { : : : }; template <typename... Ts> void fun(const Ts&... vs) { : : : }
c 2012 Andrei Alexandrescu, Facebook.
6 / 34
A New Kind: Parameter Packs
Ts is not a type; vs is not a value!
typedef Ts MyList; // error! Ts var; // error! auto copy = vs; // error!
Ts is an alias for a list of types vs is an alias for a list of values Either list may be potentially empty Both obey only specic actions
c 2012 Andrei Alexandrescu, Facebook.
7 / 34
Using Parameter Packs
Apply sizeof... to it
size_t items = sizeof...(Ts); // or vs
Expand back
template <typename... Ts> void fun(Ts&&... vs) { gun(3.14, std::forward<Ts>(vs)..., 6.28); }
Thats about it!
c 2012 Andrei Alexandrescu, Facebook.
8 / 34
Expansion rules
Use
Expansion
Ts... Ts&&... x<Ts, Y>::z... x<Ts&, Us>... func(5, vs)...
T1, . . . , Tn T1&&, . . . , Tn&& x<T1, Y>::z, . . . , x<Tn, Y>::z x<T1&, U1>, . . . , x<Tn&, Un> func(5, v1), . . . , func(5, vn)
(Please note: ellipses on the right are in a different font)
c 2012 Andrei Alexandrescu, Facebook.
9 / 34
Expansion loci (1/2)
Initializer lists
any a[] = { vs... };
Base speciers
template struct C template struct D
<typename... Ts> : Ts... {}; <typename... Ts> : Box<Ts>... { : : : };
Member initializer lists
// Inside struct D template <typename... Us> D(Us... vs) : Box<Ts>(vs)... {}
c 2012 Andrei Alexandrescu, Facebook.
10 / 34
Expansion loci (2/2)
Template argument lists
std::map<Ts...> m;
Exception specications On second thought, scratch that Attribute lists
struct [[ Ts... ]] IAmFromTheFuture {};
Capture lists
template <class... Ts> void fun(Ts... vs) { auto g = [&vs...] { return gun(vs...); } g(); }
c 2012 Andrei Alexandrescu, Facebook.
11 / 34
Multiple expansions
Expansion proceeds outwards These are different expansions!
template <class... Ts> void fun(Ts... vs) { gun(A<Ts...>::hun(vs)...); gun(A<Ts...>::hun(vs...)); gun(A<Ts>::hun(vs)...); }
c 2012 Andrei Alexandrescu, Facebook.
12 / 34
Per popular demand: VVTTs
template < typename T, template < template<class...> class... Policies > > class ICantBelieveItsNotButter;
Yup, this works.
c 2012 Andrei Alexandrescu, Facebook.
13 / 34
How to use variadics?
Pattern matching!
template <class T1, class T2> bool isOneOf(T1&& a, T2&& b) { return a == b; } template <class T1, class T2, class... Ts> bool isOneOf(T1&& a, T2&& b, Ts&&... vs) { return a == b || isOneOf(a, vs...); } assert(isOneOf(1, 2, 3.5, 4, 1, 2));
15 / 34
c 2012 Andrei Alexandrescu, Facebook.
True Variadic Functions
c 2012 Andrei Alexandrescu, Facebook.
16 / 34
Typesafe printf
Stock printf: Fast Thread-safe Convenient Ubiquitously known Utterly unsafe Conventionally: reimplement from rst principles Here: Add verication and adaptation code
c 2012 Andrei Alexandrescu, Facebook.
17 / 34
Step 1: Add adaptation routines
template <class T> typename enable_if<is_integral<T>::value, long>::type normalizeArg(T arg) { return arg; } template <class T> typename enable_if<is_floating_point<T>::value, double>::type normalizeArg(T arg) { return arg; } template <class T> typename enable_if<is_pointer<T>::value, T>::type normalizeArg(T arg) { return arg; } const char* normalizeArg(const string& arg) { return arg.c_str(); }
c 2012 Andrei Alexandrescu, Facebook.
18 / 34
Preliminary tests
// Not really safe yet template <typename... Ts> int safe_printf(const char * f, const Ts&... ts) { return printf(f, normalizeArg(ts)...); }
c 2012 Andrei Alexandrescu, Facebook.
19 / 34
Step 2: Dene test for arg-less call
void check_printf(const char * f) { for (; *f; ++f) { if (*f != % || *++f == %) continue; throw Exc("Bad format"); } }
c 2012 Andrei Alexandrescu, Facebook.
20 / 34
Step 3: Dene recursive test
template <class T, typename... Ts> void check_printf(const char * f, const T& t, const Ts&... ts) { for (; *f; ++f) { if (*f != % || *++f == %) continue; switch (*f) { default: throw Exc("Invalid format char: %", *f); case f: case g: ENFORCE(is_floating_point<T>::value); break; case s: . . . } return check_printf(++f, ts...); // AHA!!! } throw Exc("Too few format specifiers."); }
c 2012 Andrei Alexandrescu, Facebook.
21 / 34
Step 4: Integration
template <typename... Ts> int safe_printf(const char * f, const Ts&... ts) { check_printf(f, normalizeArg(ts)...); return printf(f, normalizeArg(ts)...); }
c 2012 Andrei Alexandrescu, Facebook.
22 / 34
Further improvements
Extend to all types (easy) Add ags, precision etc (easy but >1 slide) Allow odd cases (e.g. print long as pointer) Dene safe_scanf Guard the check:
#ifndef NDEBUG check_printf(f, normalizeArg(ts)...); #endif
c 2012 Andrei Alexandrescu, Facebook.
23 / 34
std::tuple
c 2012 Andrei Alexandrescu, Facebook.
24 / 34
std::tuple
Largest variadics-related offering in std Product type packing together any number of values of heterogeneous types Generalizes, plays nice with std::pair Store layout not specied + Implementation is free to choose optimally Currently neither does No prex/sufx property
c 2012 Andrei Alexandrescu, Facebook.
25 / 34
std::tuple introduction
tuple<int, string, double> t; static_assert(tuple_size<decltype(t)>::value == 3, "Rupture in the Universe."); get<0>(t) = 42; assert(get<0>(t) == 42); get<1>(t) = "forty-two"; get<2>(t) = 0.42;
c 2012 Andrei Alexandrescu, Facebook.
26 / 34
The usual suspects
Constructors, assignment make_tuple Equality and ordering comparisons swap
c 2012 Andrei Alexandrescu, Facebook.
27 / 34
Less usual suspects
pack_arguments tie tuple_cat Allocator constructors, uses_allocator Range primitives begin, end
c 2012 Andrei Alexandrescu, Facebook.
28 / 34
std::tuple structure
template <class... Ts> class tuple {}; template <class T, class... Ts> class tuple<T, Ts...> : private tuple<Ts...> { private: T head_; ... }; Head is a sufx of the structure No prescribed layout properties
c 2012 Andrei Alexandrescu, Facebook.
29 / 34
Implementing std::get
Lets rst implement the kth type template <size_t, class> struct tuple_element; template <class T, class... Ts> struct tuple_element<0, tuple<T, Ts...>> { typedef T type; }; template <size_t k, class T, class... Ts> struct tuple_element<k, tuple<T, Ts...>> { typedef typename tuple_element<k-1, tuple<Ts...>>::type type; };
c 2012 Andrei Alexandrescu, Facebook.
30 / 34
Implementing std::get: base case
Shouldnt be a member! t.template get<1>(), ew template <size_t k, class... Ts> typename enable_if<k == 0, typename tuple_element<0, tuple<Ts...>>::type&>::type get(tuple<Ts...>& t) { return t.head(); }
c 2012 Andrei Alexandrescu, Facebook.
31 / 34
Implementing std::get: recursion
template <size_t k, class T, class... Ts> typename enable_if<k != 0, typename tuple_element<k, tuple<T, Ts...>>::type&>::type get(tuple<T, Ts...>& t) { tuple<Ts...> & super = t; // get must be friend return get<k - 1>(super); }
c 2012 Andrei Alexandrescu, Facebook.
32 / 34
Summary
c 2012 Andrei Alexandrescu, Facebook.
33 / 34
Summary
Familiar approach: pattern matching with recursion Yet new, too: expansion rules and loci True variadic functions nally possible std::tuple a useful abstraction for product types
c 2012 Andrei Alexandrescu, Facebook.
34 / 34