0% found this document useful (0 votes)
238 views34 pages

GN12 Variadic Templates Are Funadic

The document discusses variadic templates and their applications. Variadic templates allow functions and classes to accept a variable number of arguments and types. They enable type-safe variadic functions and define algebraic data types like tuples more easily. The talk covers fundamentals of parameter packs, expansion rules, and how to implement true variadic functions like a typesafe printf. It also discusses how std::tuple works as a generalized product type that packs values of different types.

Uploaded by

kan181
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
238 views34 pages

GN12 Variadic Templates Are Funadic

The document discusses variadic templates and their applications. Variadic templates allow functions and classes to accept a variable number of arguments and types. They enable type-safe variadic functions and define algebraic data types like tuples more easily. The talk covers fundamentals of parameter packs, expansion rules, and how to implement true variadic functions like a typesafe printf. It also discusses how std::tuple works as a generalized product type that packs values of different types.

Uploaded by

kan181
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

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

You might also like