Top Banner
Modern C++ A (Hopefully) Practical Introduction Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st , 2013
93

Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

Mar 28, 2015

Download

Documents

Nelson Tardif
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

Modern C++A (Hopefully) Practical Introduction

Yaser ZhianDead MageIGDI, Workshop 10, May 30th-31st, 2013

Page 2: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 2

Agenda

Today: auto, decltype, range-based for, etc. Lambdas Rvalue references and moving Variadic templates

Tomorrow Threads, atomics and the memory model Other features: initializer lists, constexpr, etc. Library updates: new containers, smart pointers, etc. General Q&A

Page 3: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 3

Themes and Takeaways

Ways to write code that is: Cleaner and less error-prone Faster Richer and can do more (occasionally)

Know thy language You can never have too many tools

Elegance in interface; complexity (if any) in implementation

Take everything here with a grain of salt!

Page 4: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 4

Examples and Exercises (1/2)

We will use Visual Studio 2012 (with the Nov 2012 CTP compiler update.) Go ahead. Open it up, make a project, add a file,

set the “toolset” in project options. Write a simple “hello, world” and run it.

Please do try and write code; the sound of keyboard does not disrupt the workshop.

Page 5: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 5

Examples and Exercises (2/2)

We will also use “IDE One” online compiler at http://ideone.com/ You might want to register an account there. Do so while I talk about unimportant stuff and

answer any questions… Remember to select the C++11 compiler. Write and run a simple program here as well.

Page 6: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 6

auto, decltypeand range-based for

Page 7: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 7

decltype (1/3)

What is the type of a + b? a? int? double ? Dependent on operator +, and on a and b.▪ And a whole lot of name lookup, type deduction and

overload resolution rules. Even if you don’t know, the compiler always

does.decltype(a + b) c;c = a + b; (Instead of e.g. double c;)

Page 8: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 8

decltype (2/3)

What’s the return type of this function?template <typename T, typename U>??? Add (T const & a, U const & b) {

return a + b;}

One answer is decltype(T() + U())▪ Not entirely correct. (Why?)

The correct answer is decltype(a + b) But that won’t compile.

Page 9: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 9

decltype (3/3)

What is wrong with this?template <typename T, typename U>decltype(a + b)

Add (T const & a, U const & b) {

return a + b;}

This is basically the motivation behind the new function declaration syntax in C++11.

Page 10: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 10

New Function Syntax

auto Fun (type1 p1) -> returntype; The previous function template then becomes:

template <typename T, typename U>auto Add (T const & a, U const & b) -> decltype(a + b){ return a + b;}

This works for ordinary functions too: auto Sqr (float x)->float {return x*x;}

Page 11: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 11

auto (1/2)

Putting auto where a type name is expected, instructs the compiler to infer type from initializing expression, e.g. auto foo = a * b + c * d; auto bar = new std::map<std::string, bool>;

auto baz = new std::map< std::pair<std::string, int>, std::vector<bool, my_bool_alloc>>::const_iterator;

Page 12: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 12

auto (2/2)

Some more examples: auto x = 0; auto y = do_stuff (x); auto const & y = do_stuff (x); auto f = std::bind (foo, _1, 42); for (auto i = c.begin(), e = c.end(); i != e; ++i) {…}

Page 13: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 13

decltype and auto – a caveat

Sometimes, you have to be very careful with auto and decltype:

std::vector<int> const & v (1);auto a = v[0]; // intdecltype(v[1]) b = 1; // int const &auto c = 0; // intauto d = c; // intdecltype(c) e = 1; // intdecltype((c)) f = c; // int &decltype(0) g; // int

Page 14: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 14

Range-based for Loop (1/2)

How common is this code snippet?vector<string> v;for (vector<string>::iterator i = v.begin(); i != u.end(); i++) cout << *i << endl; How many problems can you see? Here’s a better version:for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) cout << *i << endl; This is the best version:for (auto const & s : v) cout << s << endl;

Page 15: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 15

Range-based for Loop (2/2)

This loop:for (for-range-declaration : expression) statement will get expanded to something like this:{ auto && __range = range-init; for (auto __begin= begin-expr, __end= end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement }}

Page 16: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 16

LambdasIntroducing more “functionality” into C++

Page 17: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 17

Lambdas (1/5)

Lambdas are unnamed functions that you can write almost anywhere in your code (that you can write an expression.)

For example: [] (int x) -> int {return x * x;} [] (int x,int y){return x<y ? y : x;}

What does this do? [] (double v) {cout << v;} (4.2);

Page 18: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 18

Lambdas (2/5)

Storing lambdas: auto sqr = [] (int x) -> int {return x * x;};auto a = sqr(42);

std::function<int(int, int)> g =[] (int a, int b) {return a + b;};int d = g(43, -1);

auto h = std::bind ( [](int x,int y){return x<y ? y : x;} , _1, 0);auto n = h (-7);

Page 19: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 19

Lambdas (3/5)

Consider these functions: template <typename C, typename F>void Apply (C & c, F const & f) { for (auto & v : c) f(v);}

template <typename C, typename T>void Apply2 (C & c, function<void(T&)> const & f) { for (auto & v : c) f(v);}

Used like this: int a [] = {10, 3, 17, -1};Apply (a, [] (int & x) {x += 2;});

Page 20: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 20

Lambdas (4/5)

Apply (a, [](int x) {cout << x << endl;});

int y = 2;Apply (a, [y](int & x) {x += y;});

int s = 0;Apply (a, [&s](int x) {s += x;});

Apply (a, [y, &s](int x) {s += x + y;});

Page 21: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 21

Lambdas (5/5)

int y = 2;auto f = [y](int & x) {x += y;};y = 10;Apply (a, f);

int y = 2;auto f = [&y](int & x) {x += y;};y = 10;Apply (a, f);

By the way, you can capture everything by value ([=]) or by reference ([&]).

Page 22: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 22

Rvalue References

Page 23: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 23

Rvalue References and Moving (1/B)

C++ used to have a tendency to copy stuff around if you weren’t paying attention!

What happens when we call this function? vector<string> GenerateNames (){

returnvector<string>(50, string(100,

'*'));}

A whole lot of useless stuff are created and copied around. All sorts of techniques and tricks to avoid those copies.

Page 24: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 24

Rvalue References and Moving (2/B)

string s = string("Hello") + " " + "world.";

1. string (char const *)

2. string operator + (string const &, char const *)

3. string operator + (string const &, char const *)

4. this ultimately called the copy c’tor string (string const &). In total, there can be as many as 5 (or even 7) temporary strings

here. (Unrelated note) Some allocations can be avoided with

“Expression Templates”.

Page 25: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 25

(Copy Elision and RVO)

When dealing with anonymous temporary objects, the compiler can “elide” their (copy-) construction, which is called “copy elision”.

This is a unique kind of optimization, as the compiler is allowed to remove code that has side effects!

Return Value Optimization is one kind of copy elision.

Page 26: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 26

Rvalue References and Moving (3/B)

C++11 introduces “rvalue references” to let you work with (kinda) temporary objects. Rvalue references are denoted with &&. e.g. int && p = 3; or void foo (std::string && s); or Matrix::Matrix (Matrix && that){…}

Page 27: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 27

Rvalue References and Moving (4/B)

In situations where you used to copy the data from an object into another object, if your first object is an rvalue (i.e. temporary) now you can “move” the data from that to this.

Two important usages of rvalue references are “move construction” and “move assignment”. e.g. string (string && that);// move c'tor and string & operator = (string && that); // move assignment

Page 28: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 28

Rvalue References and Moving (5/B)

template <typename T>class Matrix{private:

T * m_data;unsigned m_rows, m_columns;

public:Matrix (unsigned rows, unsigned columns);~Matrix ();Matrix (Matrix<T> const & that);template <typename U> Matrix (Matrix<U> const &

that);Matrix<T> & operator = (Matrix<T> const & that);Matrix (Matrix<T> && that);Matrix<T> & operator = (Matrix<T> && that);...

};

Page 29: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 29

Rvalue References and Moving (6/B)

template <typename T>class Matrix{

...unsigned rows () const;unsigned columns () const;unsigned size () const;T & operator () (unsigned row, unsigned col);// m(5, 7) =

0;T const & operator () (unsigned row, unsigned col) const;

template <typename U>auto operator + (Matrix<U> const & rhs) const

-> Matrix<decltype(T() + U())>;

template <typename U>auto operator * (Matrix<U> const & rhs) const

-> Matrix<decltype(T() * U() + T() * U())>;};

Page 30: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 30

Rvalue References and Moving (7/B)

Matrix (unsigned rows, unsigned columns) : m_rows (rows), m_columns (columns) , m_data (new T [rows * columns]){}

~Matrix (){ delete[] m_data;}

Matrix (Matrix<T> const & that) : m_rows (that.m_rows), m_columns (that.m_columns) , m_data (new T [that.m_rows * that.m_columns]){ std::copy ( that.m_data, that.m_data + (m_rows * m_columns), m_data );}

Page 31: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 31

Rvalue References and Moving (8/B)

Matrix<T> & operator = (Matrix<T> const & that){ if (this != &that) { T * new_data = new T [that.m_rows * that.m_columns]; std::copy ( that.m_data, that.m_data + (m_rows * m_columns), new_data ); delete[] m_data; m_data = new_data; m_rows = that.m_rows; m_columns = that.m_columns; } return *this;}

Page 32: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 32

Rvalue References and Moving (9/B)

Matrix (Matrix<T> && that) : m_rows (that.m_rows), m_columns (that.m_columns) , m_data (that.m_data){ that.m_rows = that.m_columns = 0; that.m_data = nullptr;}

Page 33: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 33

Rvalue References and Moving (A/B)

Matrix<T> & operator = (Matrix<T> && that){ if (this != &that) { delete[] m_data; m_rows = that.m_rows; m_columns = that.m_columns; m_data = that.data; that.m_rows = rhs.m_columns = 0; that.m_data = nullptr; } return *this;}

Page 34: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 34

Rvalue References and Moving (B/B)

struct SomeClass{ string s; vector<int> v;public: // WRONG! WRONG! WRONG! // Doesn’t move, just copies. SomeClass (SomeClass && that) : s (that.s), v (that.v) {}

SomeClass (SomeClass && that) : s (std::move(that.s)), v (std::move(that.v)) {}};

Page 35: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 35

std::move (1/8)

In principle, std::move should look like this:template <typename T>??? move (??? something){ return something;}

What should the argument type be? T&& ? T& ? Both? Neither?

We need to be able to pass in both lvalues and rvalues.

Page 36: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 36

std::move (2/8)

We can overload move() like this: move (T && something) move (T & something) But that will lead to exponential explosion of overloads if the function

has more arguments. “Reference collapse” rule in C++98:

int& & is collapsed to int&. In C++11, the rules are: (in addition to the above)

int&& & is collapsed to int&. int& && is collapsed to int&. int&& && is collapsed to int&&.

Page 37: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 37

std::move (3/8)

Therefore, only the T&& version should be enough. If you pass in an lvalue to our move, the actual argument

type will collapse into T&, which is what we want (probably.) So, move looks like this thus far:template <typename T>??? move (T && something){ return something;}

Page 38: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 38

std::move (4/8)

Now, what is the return type? T&& ? It should be T&& in the end. But if we declare it so, and move() is called on an lvalue,▪ then T will be SomeType&▪ then T&& will be SomeType& &&▪ then it will collapse into SomeType&▪ then we will be returning an lvalue reference from move(),

which will prevent any moving at all. We need a way to remove the & if T already has one.

Page 39: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 39

std::move (5/8)

We need a mechanism to map one type to another In this case, to map T& and T&& to T, and T to T.

There is no simple way to describe the process, but this is how it’s done: template<typename T> struct RemoveReference{ typedef T type;};

With that, RemoveReference<int>::type will be equivalent to int.

But we are not done.

Page 40: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 40

std::move (6/8)

Now we specialize:template<typename T>struct RemoveReference<T&>{ typedef T type;};

template<typename T>struct RemoveReference<T &&>{ typedef T type;};

Now, RemoveReference<int &>::type will be int too.

Page 41: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 41

std::move (7/8)

Our move now has the correct signature:template <typename T>typename RemoveReference<T>::type &&move (T && something){ return something;}

But it’s not correct. That “something” in there is an lvalue, remember?

Page 42: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 42

std::move (8/8)

…so we cast it to an rvalue reference:template <typename T>typename RemoveReference<T>::type && move (T && something){ return static_cast< typename RemoveReference<T>::type && > (something);}

Hopefully, this is correct now!

Page 43: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 43

Universal References

There is no such thing as universal references! But, due to the C++11 reference collapsing,

sometimes when you write T && v, you can get anything; both lvalues and rvalues.

These can be thought of as “universal references”.

Two preconditions: There must be T&&, And there must be type deduction.

Page 44: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 44

End of Day 1Any questions?

Page 45: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 45

ScopeGuardA Simple Method to Do RAII and Transactions

Page 46: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 46

Motivation (in C)

This is an extremely common pattern in programming: if (<action>) { if (!<next>) <rollback> <cleanup>}

For example: if (OpenDatabase()) { if (!WriteNameAndAge()) UnwriteNameAndAge(); CloseDatabase ();}

Page 47: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 47

Motivation (in C+)

The “object-oriented” way might be: class RAII { RAII () {<action>} ~RAII () {<cleanup>}};

… RAII raii;try { <next>} catch (...) { <rollback> throw;}

Page 48: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 48

Even More Motivation

What happens if you need to compose actions? if (<action1>) {

if (<action2>) {

if (!<next2>) {

<rollback2>

<rollback1>

}

<cleanup2>

} else

<rollback1>

<cleanup1>

}

Page 49: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 49

The C++ Way (1/2)

What if we could write this: <action>SCOPE_EXIT {<cleanup>};SCOPE_FAIL {<rollback>};<next>

Page 50: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 50

The C++ Way (2/2)

Extremely easy to compose: <action1>SCOPE_EXIT {<cleanup1>};SCOPE_FAIL {<rollback1>};<action2>SCOPE_EXIT {<cleanup2>};SCOPE_FAIL {<rollback2>};<next2>

Page 51: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 51

ScopeGuard (1/6)

To start, we want some way to execute code when the execution is exiting the current scope.

The key idea here is to write a class that accepts a lambda at construction and calls it at destruction.

But how do we store a lambda for later use? We can use std::function, but should we?

Page 52: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 52

ScopeGuard (2/6)

Let’s start like this:template <typename F>class ScopeGuard {public: ScopeGuard (F f) : m_f (std::move(f)) {} ~ScopeGuard () {m_f();}private: F m_f;}; And a helper function:template <typename F>ScopeGuard<F> MakeScopeGuard (F f) { return ScopeGuard<F>(std::move(f));}

Page 53: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 53

ScopeGuard (3/6)

This is used like this:int * p = new int [1000];auto g = MakeScopeGuard([&]{delete[] p;});//… Without MakeScopeGuard(), we can’t construct ScopeGuard instances that use lambdas, because they don’t have type names.

But we don’t have a way to tell scope guard not to execute its clean-up code (in case we don’t want to roll back.)

Page 54: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 54

ScopeGuard (4/6)

So we add a flag and a method to “dismiss” the scope guard when needed:template <typename F> class ScopeGuard {public: ScopeGuard (F f) : m_f (std::move(f)) , m_dismissed (false) {} ~ScopeGuard () {if (!m_dismissed) m_f();} void dismiss () {m_dismissed = true;}private: F m_f; bool m_dismissed;};

Page 55: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 55

ScopeGuard (5/6)

A very important part is missing though… A move constructor:ScopeGuard (ScopeGuard && that) : m_f (std::move(that.m_f)) , m_dismissed (std::move(that.m_dismissed)){ that.dismiss ();} And we should disallow copying, etc.private: ScopeGuard (ScopeGuard const &); ScopeGuard & operator = (ScopeGuard const &);

Page 56: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 56

ScopeGuard (6/6)

Our motivating example becomes:<action1>

auto g1 = MakeScopeGuard([&]{<cleanup1>});

auto g2 = MakeScopeGuard([&]{<rollback1>});

<action2>

auto g3 = MakeScopeGuard([&]{<cleanup2>});

auto g4 = MakeScopeGuard([&]{<rollback2>});

<next2>

g2.dismiss();g4.dismiss();

Page 57: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 57

Variadic TemplatesDo you feel lucky?!

Page 58: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 58

Variadic Templates (1/7)

Templates with variable number of arguments For exampletemplate <typename... Ts>size_t log (int severity, char const * msg, Ts&&... vs);

Remember the old way? size_t log (int severity, char const * msg, ...); Using va_list, va_start, va_arg and va_end in <cstdarg>

Or #define LOG_ERROR(msg, ...) \ log (SevError, msg, __VA_ARGS__)

Page 59: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 59

Variadic Templates (2/7)

Almost the same for classes: template <typename... Ts> class ManyParents : Ts... { ManyParents () : Ts ()... {} }; Now these are valid: ManyParents<A> a; ManyParents<A, B> b;

Page 60: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 60

Variadic Templates (3/7)

template <typename T, typename... PTs>T * Create (T * parent, PTs&&... ps){ T* ret = new T; ret->create (parent, std::forward<PTs>(ps)...); return ret;}

PTs and ps are not types, values, arrays, tuples or initializer lists.

They are new “things”.

Page 61: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 61

Variadic Templates (4/7)

Rules of expansion are very interesting: Ts... → T1,T2,…,Tn Ts&&... → T1&&,…,Tn&& A<Ts,U>... → A<T1,U>,…,A<Tn,U> A<Ts,Us>... → A<T1,U1>,…,A<Tn,Un> f(42, vs...) → f(42,v1,…,vn) f(42, vs)... → f(42,v1),…,f(42,vn)

One more operation you can do:size_t items = sizeof...(Ts); // or vs

Page 62: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 62

Variadic Templates (5/7)

Let’s implement the sizeof... operator as an example. template <typename... Ts> struct CountOf;

template <> struct CountOf<> { enum { value = 0 }; };

template <typename T, typename... Ts> struct CountOf { enum { value = CountOf<Ts...>::value + 1 }; }; Use CountOf like this: size_t items = CountOf<Ts>::value;

Page 63: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 63

Variadic Templates (6/7)

Let’s implement a function named IsOneOf() that can be used like this: IsOneOf(42, 3, -1, 3.1416, 42.0f, 0)▪ which should return true

or IsOneOf (0, "hello")▪ which should fail to compile

How do we start the implementation? Remember, think recursively!

Page 64: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 64

Variadic Templates (7/7)

template <typename A, typename T0>bool IsOneOf (A && a, T && t0){ return a == t0;}

template<typename A,typename T0,typename... Ts>bool IsOneOf (A && a, T0 && t0, Ts&&... ts){ return a == t0 || IsOneOf(a, std::forward<Ts>(ts)...);}

Page 65: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 65

C++11 Memory ModelFinally!

Page 66: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 66

What’s a Memory Model? (1/4)

The machine we code for (or want to code for): Each statement in your high-level program gets translated

into several machine instructions The (one) CPU runs the instructions in the program one by

one All interactions with memory finish before the next

instruction starts This is absolutely not true even in a single-threaded

program running on a single-CPU machine It hasn’t been true for about 2-3 decades now CPU technology, cache and memory systems and compiler

optimizations make it not true

Page 67: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 67

What’s a Memory Model? (2/4)

Even in a multi-core world, we assume that: Each CPU runs the instructions one-by-one All interactions of each CPU with memory finish

before the next instruction starts on that CPU Memory ops from different CPUs are serialized by the

memory system and effected one before the other The whole system behaves as if we were executing

some “interleaving” of all threads as a single stream of operations on a single CPU

This is even less true (if that’s at all possible!)

Page 68: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 68

What’s a Memory Model? (3/4)

YOUR COMPUTER DOES NOT EXECUTE THE PROGRAMS YOU WRITE. If it did, your programs would have been 10s or

100s of times slower It makes it appear as though your program is being

executed

Page 69: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 69

What’s a Memory Model? (4/4)

The expected behavior of hardware with respect to shared data among threads of execution Obviously important for correctness Also important for optimization If you want to have the slightest chance to know

what the heck is going on!

Page 70: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 70

Sequential Consistency

We have sequential consistency if: “the result of any execution is the same as if the

operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program.”

E.g., if A, B, C are threads in a program, This is SC: A0, A1, B0, C0, C1, A2, C2, C3, C4, B1, A3

This is not: A0, A1, B0, C0, C2, A2, C1, C3, C4, B1, A3

Page 71: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 71

Race Condition

You have a race condition if: A memory location can be simultaneously accessed

by two threads and at least one thread is a writer Memory location is defined as▪ Either a non-bitfield variable▪ Or a sequence of non-zero-length bitfields

“Simultaneously” is defined as▪ you can’t prove that one happens before the other

Remember that in case of a race condition in your code, anything can happen. Anything.

Page 72: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 72

Optimizations (1/2)

Transformations (reorder, change, add, remove) to your code: Compiler: eliminate/combine subexprs, move code around, etc. Processor: execute your code out-of-order or speculatively, etc. Caches: delay your writes, poison or share data with each other, etc.

But you don’t care about all this. What you care about are: The code that you wrote The code that gets finally executed

You don’t (usually) care who did what; you only care that your correctly-synchronized program behaves as if some sequentially-consistent interleaving of the instructions (specially memory ops) of your threads is being executed. Also, all writes are visible atomically, globally, simultaneously

Page 73: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 73

Optimizations (2/2)

Consider Peterson’s algorithm: (Both flags are atomic and initially zero)

Does this actually work?

Thread 1:

flag1 = 1; // (1)if (flag2 != 0)// (2) <resolve contention>else <in critical region?>

Thread 2:

flag2 = 1; // (3)if (flag1 != 0)// (4) <resolve contention>else <in critical region?>

Page 74: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 74

What’s Our Memory Model?

The system (compiler, processor, memory) gives you sequentially-consistent execution, as long as your program is data-race free.

This is the memory model that C++11 (and C11) expect compilers and hardware to provide for the programmer.

The memory model is a contract between programmer and the system The programmer promises to correctly synchronize her program (no

race conditions) The system promises to provide the illusion that it is executing the

program you wrote

Page 75: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 75

Acquire and Release (1/5)

Transaction: a logical op on related data that maintains an invariant Atomic: all or nothing Consistent: takes the system from one valid state to another Independent: correct in the presence of other transactions on the

same data Example: (We have two bank accounts: A and B)

Begin transaction (we acquire exclusivity)1. Add X units to account B

2. Subtract X units from account A

End transaction (we release exclusivity)

Page 76: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 76

Acquire and Release (2/5)

Critical Region (or Critical Section): Code that must be executed in isolation from rest of program A tool that is used to implement transactions

E.g., you’d implement CR using a mutex like this:mutex MX; // MX is a mutex protecting X…{ lock_guard<mutex> lock (MX); // Acquire <read/write X>} // Release Same principle using atomic variables, etc.

Page 77: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 77

Acquire and Release (3/5)

Important rule: code can’t move out of a CR E.g., if you have:

MX.lock (); // Acquirex = 42;MX.unlock (); // Release

The system can’t transform it to: x = 42;MX.lock (); // AcquireMX.unlock (); // Release

MX.lock (); // AcquireMX.unlock (); // Releasex = 42;

Page 78: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 78

Acquire and Release (4/5)

If we have: x = 7;M.lock();y = 42;M.unlock();z = 0;

Which of these can/can’t be done?

M.lock();x = 7;y = 42;z = 0;M.unlock();

M.lock();z = 0;y = 42;x = 7;M.unlock();

z = 0;M.lock();y = 42;M.unlock();x = 7;

Page 79: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 79

Acquire and Release (5/5)

A pattern emerges! For SC acquire/release: You can’t move things up across an acquire. You can’t move things down across a release. You can’t move an acquire up across a release.

Acquire and release are also called one-way barriers (or one-way fences.)

A release store makes its prior accesses visible to an acquire load that sees (pairs with) that store. Important: a release pairs with an acquire in another thread.

A mutex lock or loading from an atomic variable is an acquire. A mutex unlock or storing to an atomic variable is a release.

Page 80: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 80

Atomics, Threads and SynchronizationWeapons of Mass Destruction

Page 81: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 81

Atomics in C++

Defined in header <atomic> Use like std::atomic<T> x;

E.g, std::atomic<int> ai; or std::atomic_int ai; or, std::atomic<std::string> as;

Might use locks (spinlocks) under the hood. Check with x.is_lock_free()

No operation works on two atomics at once or return an atomic. Available ops are =, T, ++, --, +=, -=, &=, |=, ^= There is also:

T exchange (T desired, …) bool compare_exchange_strong (T& expected, T desired, …) bool compare_exchange_weak (T& expected, T desired, …)

You can also use std::atomic_flag which has test_and_set(…) and clear(…). (And don’t forget ATOMIC_FLAG_INIT.)

Page 82: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 82

Threads in C++ (1/2)

Represented by class std::thread (in header <thread>) default-constructible and movable (not copyable) template <class F, class... Args>explicit thread (F&& f, Args&&... args);

Should always call join() or detach() t.join() waits for thread t to finish its execution t.detach() detaches t from the actual running thread otherwise the destructor will terminate the program

Get information about a thread object using std::thread::id get_id () bool joinable ()

Page 83: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 83

Threads in C++ (2/2)

The static function unsigned std::thread::hardware_concurrency() returns the number of threads that the hardware can run concurrently

There is also a namespace std::this_thread with these members: std::thread::id get_id () void yield () void sleep_for (<duration>) void sleep_until (<when>)

Page 84: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 84

Mutexes and Locks

There are four types of mutexes in C++ (in header <mutex>) mutex: basic mutual exclusion device timed_mutex: provides locking with a timeout recursive_mutex: can be acquired more than once by the same thread recursive_timed_mutex

They all provide lock(), unlock() and bool try_lock() The timed versions provide bool try_lock_for (<duration>)

and bool try_lock_until (<when>) Generally, you want to use a std::lock_guard<MT> to

lock/unlock the mutex Locks the mutex on construction; unlocks on destruction

Page 85: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 85

Once!

It is not uncommon to need to do something once and exactly once, e.g., initialization of some state, setting up of some resource, etc.

Multiple threads might attempt this, because they need the result of the initialization, setup, etc.

You can use (from header <mutex>) template <typename F, typename... Args>

void call_once (std::once_flag & flag, F && f, Args&& args...);

Like this: (remember that it also acts as a barrier)std::once_flag init_done;void ThreadProc () { std::call_once (init_done, []{InitSystem();}); <rest of the procedure>}

Page 86: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 86

async and future

async() can be used to run functions asynchronously (from header <future>) template <typename F, typename... Args>std::future<RetTypeOfF> async (F && f, Args&&... args);

returns immediately, but runs f(args...) asynchronously (possibly on another thread)

e.g. future<int> t0 = async(FindMin, v); or future<int> t1 = async([&]{return FindMin(v);});

An object of type std::future<T> basically means that someone has promised to put a T in there in the future. Incidentally, the other half of future<T> is called promise<T> Key operation is T get (), which waits for the promised value.

Page 87: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 87

Using async and future

#include <future>string flip (string s) { reverse (s.begin(), s.end()); return s;}int main () { vector<future<string>> v; v.push_back (async ([] {return flip( " ,olleH");})); v.push_back (async ([] {return flip(" weN evarB");})); v.push_back (async ([] {return flip( "!dlroW");})); for (auto& i : v) cout << i.get(); cout << endl; return 0;}

Page 88: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 88

Optional<T>Really Getting Rid of NULL

Page 89: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 89

Motivation

What do you do when you have char const * get_object_name (int id), and the object

ID does not exist in your objects? unsigned get_file_size (char const * path), and

the file does not exist? double sqrt (double x), and x is negative?

You might use NULL, or “special error values” or even exceptions, but the fact remains that sometimes, you don’t want to return (or pass around) anything. You want some values to be “optional”. Aha!

Let’s write a class that allows us to work with such values…

Page 90: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 90

End of Day 2Any questions?

Page 91: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 91

Stuff We Couldn’t Get To

Implementation of Optional<T> Discussion of wrapping objects with locking General wrapping of asynchronous transactions Initializer lists and uniform initialization constexpr std::unordered_containers Smart pointers

std::unique_ptr std::shared_ptr

Implementing shared pointer

Page 92: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

http://yaserzt.com/ 92

If you write C-style code, you’ll end up with C-style bugs.

-- Bjarne Stroustrup

If you write Java-style code, you’ll have Java-level performance.

Page 93: Yaser Zhian Dead Mage IGDI, Workshop 10, May 30 th -31 st, 2013.

Any more questions?

Contact us at http://deadmage.com/And me at [email protected]