Writing Modern C Marc Grgoire Software Architect marc

  • Slides: 53
Download presentation
Writing Modern C++ Marc Grégoire Software Architect marc. gregoire@nuonsoft. com http: //www. nuonsoft. com/blog/

Writing Modern C++ Marc Grégoire Software Architect marc. gregoire@nuonsoft. com http: //www. nuonsoft. com/blog/ April 3 rd 2012

Agenda Why C++? C++ Core Value Modern C++ Resources

Agenda Why C++? C++ Core Value Modern C++ Resources

Why C++? C++ is having a kind of renaissance People are coming back to

Why C++? C++ is having a kind of renaissance People are coming back to C++ Main reason: performance / € You want to use the hardware as efficient as possible and squeeze the most out of it � Mobile devices: have limited power, use it efficiently � Datacenters: reducing power requirements directly results in saving money

C++ Core Value Efficient abstraction � Strong abstraction: Type-safe OO and templates for powerful

C++ Core Value Efficient abstraction � Strong abstraction: Type-safe OO and templates for powerful modeling, without sacrificing control and efficiency � Full control over code execution and memory: you can always express what you want to do you can always control memory and data layout exactly, if you want to � Pay-as-you go efficiency no mandatory overheads, don’t pay for what you don’t use Example: just because C++ supports virtual functions, you don’t pay a penalty for their support if you don’t use them

C++ Core Value Cross platform, cross compiler, cross operating system Performance very important “It’s

C++ Core Value Cross platform, cross compiler, cross operating system Performance very important “It’s incredibly important for C++ to be the language of performance. If there is a language lower than C++ and that has more performance, we didn’t do our job as a C++ community. ” – Herb Sutter

C++ Core Value “The going word at Facebook is that ‘reasonably written C++ code

C++ Core Value “The going word at Facebook is that ‘reasonably written C++ code just runs fast, ’ which underscores the enormous effort spent at optimizing PHP and Java code. Paradoxically, C++ code is more difficult to write than in other languages, but efficient code is a lot easier. ” – Andrei Alexandrescu

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Clean, Safe, Fast Modern C++ code can be Clean, Safe, and Fast � Clean:

Clean, Safe, Fast Modern C++ code can be Clean, Safe, and Fast � Clean: express with minimal lines of code what you want to do, just as in other modern languages, resulting in easy to read and easy to understand code � Safe: modern C++ code is exception safe, memory safe, … � Fast: because it’s C++

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Things to Unlearn If you have used C++ before, you might have to unlearn

Things to Unlearn If you have used C++ before, you might have to unlearn a couple of things Avoid low-level pointer manipulation and raw memory manipulation, in favor of higher level constructs Do not use delete / delete [], use smart pointer; it’s: � Exceptions � Leak safe free � Deterministic, unlike garbage collectors

Things to Unlearn Never do something like: FILE* f = fopen("data. ext", "w"); //.

Things to Unlearn Never do something like: FILE* f = fopen("data. ext", "w"); //. . . fclose(f); � Not exception safe! Use RAII (Resource Acquisition Is Initialization) � Write a wrapper class: Constructor opens the file Destructor automatically closes the file � Often Deterministic you can use std: : shared_ptr, even for the above example

Things to Unlearn Instead of: FILE* f = fopen("data. ext", "w"); //. . .

Things to Unlearn Instead of: FILE* f = fopen("data. ext", "w"); //. . . fclose(f); Use: shared_ptr<FILE> file. Ptr(fopen("data. ext", "w"), fclose); Or write your own wrapper

Things to Unlearn Avoid the old C-style algorithms, instead, use modern C++ algorithms

Things to Unlearn Avoid the old C-style algorithms, instead, use modern C++ algorithms

Things to Unlearn For example, qsort() is a C-style algorithm with following signature: Memory

Things to Unlearn For example, qsort() is a C-style algorithm with following signature: Memory to be sorted Number of elements in memory Number of bytes in one element void qsort (void *base, size_t num, size_t size, int (*comparator) (const void *, const void *)); Comparison function to compare two elements // Call it as follows for a double array qsort(my. Double. Array, n, sizeof(double), compare_double);

Things to Unlearn Side-note: Even though std: : sort() is a higher level construct,

Things to Unlearn Side-note: Even though std: : sort() is a higher level construct, it’s faster than qsort by a large factor (not just a few percent) Use C++ algorithms like std: : sort() Example: std: : sort(begin(vec), end(vec));

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Lambda Expressions Syntax [capture_block](parameters) mutable exception_specification -> return_type { body } � capture block:

Lambda Expressions Syntax [capture_block](parameters) mutable exception_specification -> return_type { body } � capture block: how to capture variables from enclosing scope � parameters (optional): parameter list, just like a function � mutable (optional): variables captured by-value are const, mutable makes them non-const � exception_specification (optional): = throw list � return_type (optional): the return type; if omitted If the body of the lambda expression is of the following form: { return expression; } the type of expression will become the return_type of the lambda expression. Otherwise the return_type is void

Lambda Expressions Basic example: int main() { []{cout << "Hello from Lambda" << endl;

Lambda Expressions Basic example: int main() { []{cout << "Hello from Lambda" << endl; }(); } Capture block � � � � [ ] captures nothing [=] captures all variables by value [&] captures all variables by reference [&x] captures only x by reference and nothing else [x] captures only x by value and nothing else [=, &x, &y] captures by value by default, except variables x and y, which are captured by reference [&, x] captures by reference by default, except variable x, which is captured by value

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Old C++ Versus New C++ auto type deduction Old New circle* p = new

Old C++ Versus New C++ auto type deduction Old New circle* p = new circle(42); vector<shape*> vw = load_shapes(); for (vector<circle*>: : iterator i = vw. begin(); i != vw. end(); ++i) { if (*i && **i == *p) cout << **i << " is a matchn"; } for (vector<circle*>: : iterator i = vw. begin(); i != vw. end(); ++i) { delete *i; not exception-safe } missing try/catch, delete p; __try/__finally T* shared_ptr<T> new make_shared auto p = make_shared<circle>(42); vector<shared_ptr<shape>> vw = load_shapes(); for_each (begin(vw), end(vw), [&](shared_ptr<circle>& s) { if (s && *s == *p) cout << *s << " is a matchn"; } ); no need for “delete” automatic lifetime management exception-safe for/while/do std: : algorithms [&] lambda functions

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Avoid Delete LE TE Write your code in such a way that there is

Avoid Delete LE TE Write your code in such a way that there is never a need to use delete or delete[] DE

Avoid Delete Don’t write code as follows: void foo() { My. Object* p =

Avoid Delete Don’t write code as follows: void foo() { My. Object* p = new My. Object(); //. . . delete p; } � It’s not exception safe!

Avoid Delete Instead use shared_ptr or unique_ptr: void foo() { unique_ptr<My. Object> p =

Avoid Delete Instead use shared_ptr or unique_ptr: void foo() { unique_ptr<My. Object> p = new My. Object(); //. . . } Or, even better, just do: void foo() { My. Object obj; //. . . }

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Automatic Lifetime = Efficient + Exception Safe class widget { private: gadget g; public:

Automatic Lifetime = Efficient + Exception Safe class widget { private: gadget g; public: void draw(); }; lifetime automatically tied to enclosing object no leak, exception safe void f() { widget w; /*. . . */ w. draw(); /*. . . */ } lifetime automatically tied to enclosing scope constructs w, including the w. g gadget member Automatic destruction and deallocation for w and w. g Automatic exception safety, as if “finally { w. g. dispose(); w. dispose(); }”

The Heap and Smart Pointers class gadget; class widget { private: shared_ptr<gadget> g; };

The Heap and Smart Pointers class gadget; class widget { private: shared_ptr<gadget> g; }; class gadget { private: weak_ptr<widget> w; }; shared ownership keeps gadget alive w/auto lifetime mgmt no leak, exception safe use weak_ptr to break referencecount cycles Side-note: Never use the old auto_ptr, it’s officially deprecated!

The Heap and Smart Pointersunique ownership class node { vector<unique_ptr<node>> children; node* parent; /*

The Heap and Smart Pointersunique ownership class node { vector<unique_ptr<node>> children; node* parent; /* … */ public: node( node* parent_) : parent(parent_) { children. push_back( new node(…) ); /* … */ } }; node owns its children no leak, exception safe node observes its parent plain “new” should immediately initialize another object that owns it, example unique_ptr or shared_ptr

C++ and Garbage Collection “C++ is the best language for garbage collection principally because

C++ and Garbage Collection “C++ is the best language for garbage collection principally because it creates less garbage. ” — Bjarne Stroustrup

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Containers Your default container: vector compact, efficient: cachefriendly, prefetcher-friendly C-style arrays set: Like map,

Containers Your default container: vector compact, efficient: cachefriendly, prefetcher-friendly C-style arrays set: Like map, but only keys Avoid using Instead use modern constructs such as STL containers � std: : vector � std: : array � std: : map fixed size vector � std: : unordered_set � std: : multiset � std: : unordered_map � std: : list � std: : multimap � std: : forward_list � std: : unordered_multimap � std: : set Key-value-pairs: map (tree) or unordered_map (hash)

Containers - Examples vector<string> v; v. push_back("Geddy Lee"); array<int, 50> a; a[0] = 123;

Containers - Examples vector<string> v; v. push_back("Geddy Lee"); array<int, 50> a; a[0] = 123; map<string, string> phone; phone["Alex Lifeson"] = "+1 (416) 555 -1212"; multimap<string, string> phone; phone["Neil Peart"] = "+1 (416) 555 -1212"; phone["Neil Peart"] = "+1 (905) 555 -1234"; unordered_map<string, string> phone; phone["Alex Lifeson"] = "+1 (416) 555 -1212"; unordered_multimap<string, string> phone; phone["Neil Peart"] = "+1 (416) 555 -1212"; phone["Neil Peart"] = "+1 (905) 555 -1234";

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Loops arbitrary length lambda Old bodies, just put the loop body inside the lambda

Loops arbitrary length lambda Old bodies, just put the loop body inside the lambda New for/while/do std: : algorithms [&] lambda functions for (auto i = v. begin(); i != v. end(); ++i) { /*. . . */ } for_each (begin(v), end(v), [](string& s) { for_each to visit each /*. . . */ element } ); find_if to find a match auto i = v. begin(); for (; i != v. end(); ++i) { if (*i > x && *i < y) break; } auto i = find_if(begin(v), end(v), [=](int i) { return i > x && i < y; } prefer non); member begin()/end()

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Algorithms Don’t write your own algorithms unless you have a good reason or there

Algorithms Don’t write your own algorithms unless you have a good reason or there is no standard algorithm for your use-case Prefer standard algorithms � for_each(): Your default traversal algorithm transform() � find_if(): � sort(), for not-in-place semantics Your default search algorithm lower_bound(), …: Your default sorting and searching

Algorithms Utility Algorithms � min(), max(), minmax(), swap() � minmax() example (C++11) int x

Algorithms Utility Algorithms � min(), max(), minmax(), swap() � minmax() example (C++11) int x 1 = 2, x 2 = 9, x 3 = 3, x 4 = 12; pair<int, int> p 1 = minmax({x 1, x 2, x 3, x 4}); cout << "Minmax of 4 elements is " << p 1. first << ", " << p 1. second << endl;

Algorithms Nonmodifying algorithms � Search algorithms find(), find_if_not(), find_first_of(), search_n(), lower_bound(), upper_bound(), equal_range(), minmax_element(),

Algorithms Nonmodifying algorithms � Search algorithms find(), find_if_not(), find_first_of(), search_n(), lower_bound(), upper_bound(), equal_range(), minmax_element(), all_of(), any_of(), none_of(), … � Numerical processing algorithms count(), count_if(), accumulate(), inner_product(), … � Comparison equal(), mismatch(), … � Operational algorithms for_each() algorithms

Algorithms Nonmodifying algorithms examples // Find min and max with 1 algorithm auto minmax

Algorithms Nonmodifying algorithms examples // Find min and max with 1 algorithm auto minmax = minmax_element(begin(vec), end(vec)); cout << "Min = " << *(minmax. first) << " and Max = " << *(minmax. second) << endl; // Find the first subsequence of two consecutive 8 s auto it = search_n(begin(vec), end(vec), 2, 8); // all_of() vector<int> vec = {1, 1, 1, 1}; bool b = all_of(begin(vec), end(vec), [](int i){return i == 1; });

Algorithms Numerical processing algorithms examples // Calculate arithmetic mean of the elements in a

Algorithms Numerical processing algorithms examples // Calculate arithmetic mean of the elements in a vector double sum = accumulate(begin(vec), end(vec), 0); double avg = sum / vec. size(); // Calculate geometric mean of the elements in a vector double mult = accumulate(begin(vec), end(vec), 1, [](int num 1, int num 2){return num 1 * num 2; }); double geomean = pow(mult, 1. 0 / vec. size()); // Create a vector with values 5 6 7 8 9 10 11 12 13 14 vector<int> vec(10); iota(begin(vec), end(vec), 5);

Algorithms Modifying algorithms � transform(), copy(), copy_if(), move(), swap_ranges(), replace_if(), fill(), generate(), remove_if(), reverse(),

Algorithms Modifying algorithms � transform(), copy(), copy_if(), move(), swap_ranges(), replace_if(), fill(), generate(), remove_if(), reverse(), rotate(), next_permutation(), …

Algorithms Modifying algorithms examples // Add 100 to each element in the vector transform(begin(vec),

Algorithms Modifying algorithms examples // Add 100 to each element in the vector transform(begin(vec), end(vec), begin(vec), [](int i){return i + 100; }); // Replace all values < 0 with 0 replace_if(begin(vec), end(vec), [](int i){return i < 0; }, 0); // Remove all empty strings from a vector of strings // (Use remove-erase pattern!) auto it = remove_if(begin(strings), end(strings), [](const string& str){return str. empty(); }); // erase the removed elements strings. erase(it, strings. end());

Algorithms Sorting algorithms � sort(), stable_sort(), partial_sort(), merge(), … Set algorithms � set_union(), set_intersection(),

Algorithms Sorting algorithms � sort(), stable_sort(), partial_sort(), merge(), … Set algorithms � set_union(), set_intersection(), set_difference(), set_symmetric_difference(), …

Algorithms Sorting algorithms example � If you want to do some binary search (lower_bound,

Algorithms Sorting algorithms example � If you want to do some binary search (lower_bound, upper_bound, equal_range, …), the sequence should be sorted first � Be sure to sort the sequence with the same predicate as you give to the search algorithm � Use named lambda, example: auto comp = [](const widget& w 1, const widget& w 2) { return w 1. weight() < w 2. weight(); } sort(begin(v), end(v), comp); auto i = lower_bound(begin(v), end(v), w, comp);

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Move Semantics C++11 Move Semantics increases efficiency and results in cleaner, easier to understand

Move Semantics C++11 Move Semantics increases efficiency and results in cleaner, easier to understand code Why is move semantics useful?

Move Semantics – Why useful? set<widget> load_huge_data() { set<widget> ret; // … load data

Move Semantics – Why useful? set<widget> load_huge_data() { set<widget> ret; // … load data and populate ret … return ret; } vector<string> v = AMillion. Strings(); v. insert(begin(v)+v. size()/2, "tom"); v. insert(begin(v)+v. size()/2, "richard"); v. insert(begin(v)+v. size()/2, "harry"); efficient, no deep copyshuffle (just 1. 5 M ptr/len assignments) widgets = load_huge_data(); efficient, no deep copy no need for “heap allocation + return a pointer” workaround Huge. Matrix operator+( const Huge. Matrix&, const Huge. Matrix& ); hm 3 = hm 1+hm 2; efficient, no extra copies

Move Semantics – How To Implement? Needs: � � Move constructor Move assignment operator

Move Semantics – How To Implement? Needs: � � Move constructor Move assignment operator class my_class { unique_ptr<Big. Huge. Data> data; public: /*. . . */ my_class(my_class&& other) : data(move(other. data)) { } my_class& operator=(my_class&& other) { data = move(other. data); } check (if appropriate) }; void method() { if (!data) throw "moved-from object"; }

Move Semantics – When? If you have a copy constructor or copy assignment operator:

Move Semantics – When? If you have a copy constructor or copy assignment operator: Also implement move versions if they can be cheaper than a deep copy Some types have only move versions, and no copy versions � For example: some types are naturally move-only, such as unique_ptr

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new

Modern C++ Clean, Safe, Fast Things to unlearn Lambda expressions Old C++ versus new C++ Avoid delete Automatic lifetime (stack & heap) Containers Loops Algorithms Move semantics Compile time encapsulation

Compile Time Encapsulation? Use the Pimpl idiom to truly hide private members class my_class

Compile Time Encapsulation? Use the Pimpl idiom to truly hide private members class my_class { //. . . all public and protected stuff goes here. . . private: class impl; unique_ptr<impl> pimpl; // opaque type here }; my_class. h class my_class: : impl { // defined privately here //. . . all private data and functions: all of these // can now change without recompiling callers. . . }; my_class: : my_class() : pimpl(new impl) my_class. cpp { /*. . . set impl values. . . */ } Avoids rebuild cascades � Most appropriate for types used often �

Resources “Writing modern C++ code: how C++ has evolved over the years” – Herb

Resources “Writing modern C++ code: how C++ has evolved over the years” – Herb Sutter � “Going. Native 2012, Keynote: C++ Style” – Bjarne Stroustrup � http: //channel 9. msdn. com/Events/Going. Native-2012/Keynote. Bjarne-Stroustrup-Cpp 11 -Style Presentations from Going. Native 2012 � http: //channel 9. msdn. com/Events/BUILD 2011/TOOL-835 T http: //channel 9. msdn. com/Events/Going. Native-2012 Professional C++, 2 nd Edition � http: //www. wiley. com/Wiley. CDA/Wiley. Title/product. Cd-0470932449. html

Questions ? I would like to thank Herb Sutter from Microsoft for his permission

Questions ? I would like to thank Herb Sutter from Microsoft for his permission to base this presentation on one that he wrote for Build 2011.