30

modern-cpp - Metaparadigmmclark/modern-cpp.pdf · struct Factorial { enum { value = N * Factorial

Embed Size (px)

Citation preview

Modern C++(less == more) || (more == more)

!mailto:[email protected]

History

1950 1960 1970 1980 1990 2000 2010 2020

1958!!ALGOL!58!!(Bauer,(Backus(et(al)

1957!!FORTRAN!!(John(Backus)

1969!!C!!(Dennis(Ritchie)

1968!!ALGOL!68

1966!!FORTRAN!66

1983!!AT&T!C++!/!Cfront!!(Bjarne(Stroustrup)

1981!!Objec:ve<C!!(Brad(Cox(and(Tom(Love)

1979!!C!with!Classes!!(Bjarne(Stroustrup)

1978!!K&R!C!!(Brian(Kernighan(and(Dennis(Ritchie)

1977!!FORTRAN!77

1989!!ANSI/ISO!C89

1987!!GNU!C!Released

1999!ISO!C99

1998!!ISO!C++98

2003!!ISO!C++03

2011!!ISO!C++11

2014!!ISO!C++14

2017!!ISO!C++17

2008!!Clang!Released2008!!ISO!C++!TR1

What is C++?• A general purpose programming language • Has a strong bias towards systems programming • One layer above assembly language • Multi-paradigm language

• Imperative (C99 superset) • Object-oriented (polymorphism, inheritance) • Functional (immutability, lambdas, currying) • Meta-programming (templates, algorithms)

int do_rdrand() { int r; asm("retry: rdrand eax\n” " jnc retry;\n" : "=a" (r)); return r; }

Why C++• ISO/IEC 14882:2011

• International standards organisation represented by national bodies from 164 member countries

• Not open to manipulation by individuals or corporations for proprietary control

• COBOL, Fortran, Ada, Prolog, C, C++, Forth, ECMAscript

• C++ is a software engineering language that enables the production of high performance type-safe, statically verifiable native software.

Why C++• C++ is popular

Source: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

C++ projects• GNU Compiler Collection, LLVM, Clang • Java Virtual Machine, Dart, V8, Facebook HHVM • Webkit, Chrome, Safari, Firefox, Internet Explorer • MySQL, LevelDB, MongoDB, SAP DB • Adobe, Autodesk, Apple, Microsoft, … • Scientific computing, Computer Aided Engineering,

Operations Research, Telecommunications, … • Bitcoin ☺

What is Modern C++?• Modern C++!

• feels like “a new language” (Bjarne Stroustrup) • C++11 refines the C++ language in a backwards

compatible way to support existing C++ code while enabling simpler and more modern idioms

• Pass-by-value everywhere • STL containers got smarter with r-value references

• Lambdas, initializer lists, variadic templates, etc • Improved memory management • Improved standard library

Less is exponentially more“I was asked a few weeks ago, "What was the biggest surprise you encountered rolling out Go?" I knew the answer instantly: Although we expected C++ programmers to see Go as an alternative, instead most Go programmers come from languages like Python and Ruby. Very few come from C++. We—Ken, Robert and myself—were C++ programmers when we designed a new language to solve the problems that we thought needed to be solved for the kind of software we wrote. It seems almost paradoxical that other C++ programmers don't seem to care.” –Rob Pikehttp://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html

C++11 has got more

auto lambda

<random>

<type_traits>

<memory>

std::shared_ptr <thread> constexpr

decltype

<mutex> <regex>

std::tuple

std::unordered_set

std::unordered_map nullptr

static_assert <atomic>

std::unique_ptr

std::async

std::future

<functional>

<chrono>

C++11 has got less #include <iostream> #include <vector> ! using namespace std; ! int main() { vector<int> v; v.push_back(2); v.push_back(3); v.push_back(5); v.push_back(7); vector<int>::iterator i; for (i = v.begin(); i != v.end(); i++) { cout << *i << endl; } }

#include <iostream> #include <vector> ! using namespace std; ! int main() { vector<int> v = {2,3,5,7}; for (auto a : v) { cout << a << endl; } }

C++98 C++11

int main() { for (auto a : {2,3,5,7}) cout << a << endl; }

or even less

Template Metaprogramming• The C++ template system is turing complete at compile time

#include <iostream> // http://stackoverflow.com/questions/3082113/calculating-factorial-using-template-meta-programming ! using namespace std; ! template <int N> struct Factorial { enum { value = N * Factorial<N - 1>::value }; }; ! template <> struct Factorial<0> { enum { value = 1 }; }; ! int main() { cout << Factorial<4>::value << endl; cout << Factorial<0>::value << endl; }

Zero-copy layoutOO Language C++11 class Price { int timestamp; int volume; double price; Price(int timestamp, int volume, double price) { this.timestamp = timestamp; this.volume = volume; this.price = price; } static void main(String args[]) { ArrayList<Price> prices; prices.ensureCapacity(4); prices.add(new Price(1307725482, 5, 26.05)); prices.add(new Price(1307725483, 40, 26.15)); prices.add(new Price(1307725493, 100, 26.1499)); prices.add(new Price(1307725493, 112, 26.15)); } }

struct Price { int timestamp; int volume; double price; Price(int timestamp, int volume, double price) : timestamp(timestamp), volume(volume), price(price) {} }; ! int main() { vector<Price> prices; prices.reserve(4); prices.emplace_back(1307725482, 5, 26.05); prices.emplace_back(1307725483, 40, 26.15); prices.emplace_back(1307725493, 100, 26.1499); prices.emplace_back(1307725493, 112, 26.15); }

• Containers use polymorphism and type erase • Need to devolve to structure of primitive arrays

to get reasonable performance, but SoA hasbad cache behaviour

• Looks are deceiving • Templates instantiate optimized code for the type • Superset - allows implementation of polymorphic

containers i.e. vector<object>

Zero-copy layoutOO Language - 176 bytes, 5 allocations

C++11 - 72 bytes, 2 allocations

length

array object reference

Opaque Object Header (16-bytes)

64-bit Object Reference (8-bytes) 64-bit Object Reference (8-bytes) 64-bit Object Reference (8-bytes)

Opaque Object Header (16-bytes)

Opaque Object Header (16-bytes)

1307725482 5 26.05

1307725483 40 26.15

1307725493 100 26.1499

Opaque Object Header (16-bytes) size

Opaque Object Header (16-bytes)

1307725482 5 26.05 1307725483 40 26.15 1307725493 100 26.1499

array ptr size capacity

No more leaks• std::unique_ptr

• Singleton pointer wrapper • Ensures a single copy of an object • No performance overhead • Automatic release (no more delete)

• std::shared_ptr • Reference counting pointer wrapper • Thread-safe reference counter • Acts like a normal pointer but has overhead • Use when correctness is more important than performance • Automatic release (no more delete)

R-value references• template<typename T> void foo(T&&)

• New signature to detect r-values (temporaries) • In a nut shell enables compile time detection of

r-value temporaries (versus l-values) to implement efficientzero-copy move semantics and perfect forwarding

• What is an r-value?

• Adds complexity to hide complexity • Feature designed for STL and library writers • std::forward, std::move

• Efficient pass by value and return by value for containers • Simplifies idiom of user code

vector<Price> raise(vector<Price> prices) { /* does something */ }

raise({Price(1307725482, 5, 26.05), Price(1307725483, 40, 26.15)});

Initializer lists • Array initialization

was a C++98 bugbear • Adds new initializer syntax

foo{1,2,3,4} • Can be used in constructor

member initialization • New header

<initializer_list> • New template

initializer_list<T>

struct myclass { myclass (int,int); myclass (initializer_list<int>); /* definitions ... */ }; ! myclass foo {10,20}; // calls initializer_list ctor myclass bar (10,20); // calls first constructor

template <typename T> struct vec { vec(T x) : m{x, 0, 0, 1} {} vec(T x, T y) : m{x, y, 0, 1} {} vec(T x, T y, T z) : m{x, y, z, 1} {} vec(T x, T y, T z, T w) : m{x, y, z, w} {} T m[4]; }; ! vec<float> a(1, 2, 3);

Delegating constructors• Really. C++98 didn’t have them

template <typename T> struct vec { vec(T x) : vec(x, 0, 0, 1) {} vec(T x, T y) : vec(x, y, 0, 1) {} vec(T x, T y, T z) : vec(x, y, z, 1) {} vec(T x, T y, T z, T w) : m{x, y, z, w} {} T m[4]; };

Threads and Futures

• std::thread • std::async • std::future • C++ is in now line

with other modern languages threadcapabilities

#include <iostream> #include <vector> #include <algorithm> #include <numeric> #include <future> ! // source http://en.cppreference.com/w/cpp/thread/async ! template <typename I> int parallel_sum(I beg, I end) { typename I::difference_type len = end - beg; if (len < 1000) { return std::accumulate(beg, end, 0); } I mid = beg + len/2; auto handle = std::async(std::launch::async, parallel_sum<I>, mid, end); int sum = parallel_sum(beg, mid); return sum + handle.get(); } ! int main() { std::vector<int> v(10000, 1); std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << ‘\n'; }

Compile-time type identification• New header <type_traits> • std::is_void<T> • std::is_integral<T> • std::is_array<T> • std::is_class<T> • std::is_pointer<T> • std::is_volatile<T> • std::rank<T>, std::extent<T,N> • …. many many more ….

template <typename R> static const EGType& typeOf() { typedef typename std::remove_extent<R>::type A; typedef typename std::remove_pointer<R>::type P; if (std::is_array<R>::value) { typedef typename std::remove_const<A>::type V; return arrayType<V>(); } else if (std::is_pointer<R>::value) { typedef typename std::remove_const<P>::type V; return pointerType<V>(); } else { typedef typename std::remove_const<R>::type V; return integralType<V>(); } }

Variadic templates• Variadic templates allow

implementation of a compile time type safe printf(…)

• Variadic templates combined with compile time type identification gives super powers to C++

• Can collect type information at compile time and use at runtime

• Allows creation of an Objective-C style messaging system with late-binding

#include <type_traits> #include <functional> #include <string> #include <iostream> #include <iomanip> ! using namespace std; using namespace std::placeholders; ! template<typename T> void reflect_compiler_reflect(T value) { cout << setw(70) << typeid(T).name() << " is_pointer=" << is_pointer<T>::value << " is_integral=" << is_integral<T>::value << " is_floating_point=" << is_floating_point<T>::value << " is_class=" << is_class<T>::value << " is_empty=" << is_empty<T>::value << endl; } ! template<typename T, typename... Args> void reflect_compiler_reflect(T value, Args... args) { reflect_compiler_reflect(value); reflect_compiler_reflect(args...); } ! int main() { int a = 1; float b = 4.5f; double c[71]; string d; auto e = [] (int a) {}; auto f = std::bind(e, 99, _1); struct {} g; reflect_compiler_reflect(a, b, c, d, e, f, g); }

User-defined literals• Allow definition of type

safe SI units and other creative uses

• Solve Mars Climate Orbiter type problems class Symbol { public: string name; Symbol(const char *name) : name(name) {} }; ! Symbol operator "" _symbol(const char* name, size_t) { return Symbol(name); } ! int main() { Symbol Foo = "Foo"_symbol; }

More functional• lambdas

• [], [=], [&] • use in place of old-style function objects • combine with existing STL generic algorithms

e.g. std::partition • currying

• std::function • std::bind • std::placeholders::_1

Lots more STL• Compile time reflection

• <type_traits> • Containers

• <array>, <unordered_set>, <unordered_map>, <forward_list> • Multi-threading

• <atomic>, <thread>, <mutex>, <future>, <condition_variable> • More utility

• <tuple>, <regex>, <chrono>, <codecvt> • Numerics

• <random>, <ratio>, <cfenv>

C++14

• Generic lambdas • lambda initializers • Variable templates • make_unique • Runtime sized arrays • Filesystem (Boost::filesystem) • Networking

auto lambda = [](auto x, auto y) {return x + y;};

auto lambda = [value = 1] {return value;};

template<typename T> constexpr T pi = T(3.1415926535897932385);

auto u = make_unique<some_type>(ctor, params);

void foo(size_t n) { int a[n]; }

LLVM / Clang• Modular compiler toolchain • 2012 ACM Software System Award • Clang memory sanitizer • Clang address sanitizer • Clang thread sanitizer • Clang modernize • Firefox - emscripten C++ to asm.js • Chrome - PNaCl C++ LLVM bitcode • Foundation of many OpenCL implementations

C++ - pros• Maturity and Performance

• 30 years of evolution and production use in large scale systems • ~ 2x - 3x faster than Java • ~ 10x - 100x faster than Ruby and Python

• Compiler and architecture choices • GCC 4.9, Clang 3.4, Intel C++ 14, Visual C++ 2013, etc

• Easy access to processor features • SSE4 Streaming Extensions, AVX-512 SIMD Vector Extensions

• Compile time static checking and type safety • Static analysis tools, valgrind, clang memory sanitizer,…

• Multi-paradigm • Offers a choice of programming styles

C++ - cons• Big language, big learning curve

• Define an idiom with C++: google c++ style • Long compile times

• Idea is to spend time now to save it in the future • Missing standard libraries

• networking, database, graphics, sound, xml and json serialization, etc

• The cost of runtime performance • Buffer overflows,Illegal memory accesses • Requires static analysis • Potential solution: Intel MPX (Memory Protection Extensions)

Conclusion

• Pick the right language for the job • C++ is suitable for infrastructure code where

performance and type safety are important • C++ requires care with memory management

(leaks, buffer offer-flows, off-by-one errors) • Give C++ a go!

Comments / Questions