modern-cpp - Metaparadigmmclark/modern-cpp.pdf · struct Factorial { enum { value = N * Factorial
Transcript of modern-cpp - Metaparadigmmclark/modern-cpp.pdf · struct Factorial { enum { value = N * Factorial
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!