This is why we can(t) have nice things Timo van der Kuil - - PowerPoint PPT Presentation

this is why we can t have nice things
SMART_READER_LITE
LIVE PREVIEW

This is why we can(t) have nice things Timo van der Kuil - - PowerPoint PPT Presentation

This is why we can(t) have nice things Timo van der Kuil 16/11/2019 Meeting C++ 1 Who am I Student Student assistant Research intern Saxion University of Applied Sciences 2 Disclaimer First talk at a conference


slide-1
SLIDE 1

This is why we can(‘t) have nice things

Timo van der Kuil

1

16/11/2019 Meeting C++

slide-2
SLIDE 2

Who am I

  • Student
  • Student assistant
  • Research intern
  • Saxion University of Applied Sciences

2

slide-3
SLIDE 3

Disclaimer

  • First talk at a conference
  • Feedback
  • Opinions are my own

3

slide-4
SLIDE 4
  • Weird things
  • Origins
  • Design and philosophy
  • Flexibility
  • Weird things explained

4

Outline

slide-5
SLIDE 5

Weird things in C++

5

slide-6
SLIDE 6

Initialization

6

slide-7
SLIDE 7

Initialization

7

int a; std::array<int, 100> array; // a is not initialized, only declared // array is not initialized, only declared int a{}; std::array<int, 100> array{}; // array is initialized with 0’s // a is initialized with 0

slide-8
SLIDE 8

Unspecified behaviour

8

int main() { f(a(), b(), c()); } a b c? c b a? int a() { return std::puts("a"); } int b() { return std::puts("b"); } int c() { return std::puts("c"); } void f(int, int, int) {}

slide-9
SLIDE 9

More unspecified behaviour

9

int main() { return a() + b() + c(); } a b c? c b a? int a() { return std::puts("a"); } int b() { return std::puts("b"); } int c() { return std::puts("c"); }

slide-10
SLIDE 10

void

10

void f0(int i) { } int f1(void) { return 1; } // Void as return type -> no return // Void as parameter -> no parameters // Void* as parameter -> // pointer to anything (void) some_unused_var // Void as cast -> cast to nothing int f2(void* i) { return *static_cast<int*>(i); }

slide-11
SLIDE 11

mutable lambdas

11

int i = 2; auto ok = [&i](){ ++i; }; auto err = [i](){ ++i; }; auto err2 = [x{22}](){ ++x; }; auto ok2 = [i, x{22}]() mutable { ++i; ++x; };

// i captured by reference // increment of read-only variable 'i' // increment of read-only variable 'x' // Using mutable keyword

slide-12
SLIDE 12

future.h

12

std::cout << "main thread" << '\n'; first thread second thread main thread std::async(std::launch::async,[]{ std::this_thread::sleep_for(std::chrono::seconds(2)); std::cout << "first thread" << '\n'; }); std::async(std::launch::async,[]{ std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "second thread" << '\n'; });

slide-13
SLIDE 13

Type punning through unions

13

union Pun { int x; unsigned char c[sizeof(int)]; }; void bad(Pun& u) { u.x = 'x’; std::cout << u.c[0] << '\n'; // undefined behaviour }

slide-14
SLIDE 14

Origins of C++

  • A brief history -

14

slide-15
SLIDE 15

Idea for a suitable tool

15

  • Best of both worlds
  • Simula
  • Classes
  • Hierarchies
  • Concurrency
  • Static type checking
  • BCPL
  • Efficiency
  • Combining compiled programs
  • Portable implementation
slide-16
SLIDE 16

C with classes

16

Issue that called for a new tool Cpre C with classes

slide-17
SLIDE 17

From C with classes to C++

17

  • C with classes was a medium success
  • Paid for itself and developer
  • Not for support and development
  • Two choices:

1. Stop supporting the language to be able to do something else

  • 2. Develop a new language that appeals to a larger audience to pay for its

support and further development

slide-18
SLIDE 18

Usage of C++

18

(github.com, 2019)

slide-19
SLIDE 19

19

Javascript 1995 Python 1991 Java 1995 PHP 1995 C# 2001 Typescript 2012 Shell 1989 (bash) C 1972 C++ 1979

slide-20
SLIDE 20

"C is clearly not the cleanest language ever designed nor the easiest to use, so why do so many people use it?“

  • Bjarne Stroustrup, 1987

20

slide-21
SLIDE 21

Why C?

21

  • C is flexible
  • Almost every application
  • C is efficient
  • C is low level, relatively easy to make the most out of resources
  • C is available
  • There is a compiler for pretty much every platform
  • C is portable
  • Porting from OS to OS is typically feasible, but not trivial
slide-22
SLIDE 22

Design and philosophy of C++

22

slide-23
SLIDE 23

Aims of C++

23

  • Make programming more enjoyable
  • General purpose programming language that
  • Is a better C
  • Supports data abstraction
  • Supports object-oriented programming
slide-24
SLIDE 24

Development rules of C++

24

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-25
SLIDE 25

Development rules of C++

25

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-26
SLIDE 26

Development rules of C++

26

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-27
SLIDE 27

Development rules of C++

27

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-28
SLIDE 28

Development rules of C++

28

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-29
SLIDE 29

Development rules of C++

29

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-30
SLIDE 30

Development rules of C++

30

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-31
SLIDE 31

Development rules of C++

31

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-32
SLIDE 32

Development rules of C++

32

1. Evolution must be driven by real problems

  • 2. Don’t get involved in a sterile quest for perfection
  • 3. Must be useful now
  • 4. Every feature must have a reasonably obvious implementation
  • 5. Always provide a transition path
  • 6. It’s a language, not a system
  • 7. Provide comprehensive support for each supported style
  • 8. Don’t try to force people
slide-33
SLIDE 33

Flexibility

33

slide-34
SLIDE 34

Type system

34

  • Strongly and statically typed
  • Type specifiers -> Compile time checking
  • Fundamental types
  • char, double, int
  • Compound types
  • ‘Defined in terms of another type’
  • Every type is treated equally
  • The C++ Type System is your Friend by Hubert Matthews
slide-35
SLIDE 35

Type system

35

class Year {}; class Month {}; class Day {}; class Date{ Date(Year, Month, Day) {}; }; class Date{ Date(int, int, int) {}; }; // Ambiguous: d/m/y y/m/d m/d/y? // -> bug at runtime // Umambiguous: y/m/d // -> bug at compile time

slide-36
SLIDE 36

Memory model

36

  • ‘The Memory Model’ by Rainer Grimm
  • First only sequential execution -> no need for memory model
  • C++11 multi-threading
  • Race conditions
  • Every thread has r/w to memory
  • 6 memory orders
  • std::atomic
slide-37
SLIDE 37

Memory model

37

  • memory_order_seq_cst
  • > Default, strict
  • memory_order_acq_rel
  • > No reordering before and after
  • memory_order_acquire
  • > No reordering before
  • memory_order_release
  • > No reordering after
  • memory_order_consume
  • > No reordering before and after (of this atomic)
  • memory_order_relaxed
  • > Weak
  • Less rules -> more optimization
  • Up to the programmer
slide-38
SLIDE 38

Why things are weird in C++

38

slide-39
SLIDE 39

Initialization

39

int a; std::array<int, 100> array; // a is not initialized, only declared // array is not initialized, only declared int a{}; std::array<int, 100> array{}; // array is initialized with 0’s // a is initialized with 0

slide-40
SLIDE 40

Initialization

40

  • Inherited from C
  • Initialization can lead to performance hits
  • Mostly on older systems
  • std::array -> implicit, default, trivial constructor (POD)
  • Empty ctor but value initialized with {} or ()
  • Wrapper for C-style array
  • MSVC debug vs. release mode
  • ‘Initialization in C++’ by Timur Doumler
  • ‘The nightmare of initialization in C++’ by Nicolai Josuttis
slide-41
SLIDE 41

Unspecified behaviour

41

int main() { f(a(), b(), c()); } a b c? c b a? int a() { return std::puts("a"); } int b() { return std::puts("b"); } int c() { return std::puts("c"); } void f(int, int, int) {}

slide-42
SLIDE 42

More unspecified behaviour

42

int main() { return a() + b() + c(); } a b c? c b a? int a() { return std::puts("a"); } int b() { return std::puts("b"); } int c() { return std::puts("c"); }

slide-43
SLIDE 43

Unspecified behaviour

43

  • Not specified in ISO standard
  • Comma is not a sequence point
  • Mistakes can easily be avoided
  • Warnings
  • Don’t force compilers

f(a(), b(), c());

slide-44
SLIDE 44

void

44

void f0(int i) { } int f1(void) { return 1; } int f2(void* i) { return *static_cast<int*>(i); } // Void as return type -> no return // Void as parameter -> no parameters // Void* as parameter -> // pointer to anything (void) some_unused_var // Void as cast -> cast to nothing

slide-45
SLIDE 45

void

45

  • Inherited from C
  • Used for polymorphism in C
  • C++ has templates, std::optional, std::variant
  • Certain platforms still need void*; limited access to header
slide-46
SLIDE 46

void

46

C: f() -> Any amount of arguments (marked obsolescent) f(void) -> No arguments C++: f() -> No arguments f(void) -> No arguments

slide-47
SLIDE 47

mutable lambdas

47

int i = 2; auto ok = [&i](){ ++i; }; auto err = [i](){ ++i; }; auto err2 = [x{22}](){ ++x; }; auto ok2 = [i, x{22}]() mutable { ++i; ++x; };

// i captured by reference // Using mutable keyword // increment of read-only variable 'i' // increment of read-only variable 'x'

slide-48
SLIDE 48

mutable lambdas

48

  • Unnamed class
  • ::operator() is implicitly defined const
  • UNLESS mutable is used
  • Member variables
  • Prevent unwanted mutation of lambdas
slide-49
SLIDE 49

mutable lambdas

49

int x{5}; int y{}; auto lambda = [x]() mutable {return x++ + 5; }; y = lambda(); std::cout << y << ’,’ << x << '\n'; x = 20; y = lambda(); std::cout << y << ’,’ << x << '\n';

slide-50
SLIDE 50

mutable lambdas

50

struct UnnamedType { // Member variable that stores captured val int x; // Ctor initializes x with captured val (5) UnnamedType(int x) : x{x} {} // operator() executes the passed statements int operator() () { return x++ + 5; } }; auto lambda = [x]() mutable {return x++ + 5; };

slide-51
SLIDE 51

mutable lambdas

51

struct UnnamedType { // Member variable that stores captured val const int x; // Ctor initializes x with captured val (5) UnnamedType(int x) : x{x} {} // operator() executes the passed statements int operator() () const { return x++ + 5; } }; auto lambda = [x]() mutable {return x++ + 5; };

slide-52
SLIDE 52

mutable lambdas

52

int x{5}; int y{}; auto lambda = [x]() mutable {return x++ + 5; }; y = lambda(); std::cout << y << ’,’ << x << '\n'; x = 20; y = lambda(); std::cout << y << ’,’ << x << '\n'; //outputs 11,20 //outputs 10,5

slide-53
SLIDE 53

mutable lambdas

53

  • Only two ways to capture -> explicit choice by the programmer
  • Copy-by-value [=, val]
  • Reference [&, &val]
  • N3424: Lambda Correctness and Usability Issues by Herb Sutter

auto lambda = [x]() mutable {return x++ + 5; }; auto lambda = [x]() {return x + 5; }; auto lambda = [x]() const {return x + 5; }; auto lambda = [x]() {return x++ + 5; };

slide-54
SLIDE 54

future.h

54

std::cout << "main thread" << '\n'; first thread second thread main thread std::async(std::launch::async,[]{ std::this_thread::sleep_for(std::chrono::seconds(2)); std::cout << "first thread" << '\n'; }); std::async(std::launch::async,[]{ std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "second thread" << '\n'; });

slide-55
SLIDE 55

future.h

55

std::cout << "main thread" << '\n'; main thread second thread first thread auto first = std::async(std::launch::async,[]{ std::this_thread::sleep_for(std::chrono::seconds(2)); std::cout << "first thread" << '\n'; }); auto second = std::async(std::launch::async,[]{ std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "second thread" << '\n'; });

slide-56
SLIDE 56

future.h

56

  • std::async returns a std::future
  • When return discarded-> ~future implicitly called
  • Prevent detached thread executing past the main
  • When return not discarded -> ~future called at end of function
  • N2802: A plea to reconsider detach-on-destruction for thread objects by Hans Boehm
  • N3630: async, ~future, and ~thread (Revision 1) by Herb Sutter
  • N3636: ~thread Should Join by Herb Sutter
  • N3637: async and ~future (Revision 3) by Herb Sutter, Chandler Carruth, Niklas Gustafsson
  • N3679: Async() future destructors must wait by Hans Boehm
  • N3773: async and ~future (Revision 4) by Herb Sutter, Chandler Carruth, Niklas Gustafsson
  • N3776: Wording for ~future by Herb Sutter
  • N3777: Wording for deprecating async by Herb Sutter
slide-57
SLIDE 57

future.h

57

  • One proposal to save us all
  • Initially C++20, now C++23 or even C++26
  • Relies on executors
  • P1054r0: A Unified Futures Proposal For C++ by Lee Howes,

Bryce Adelstein Lelbach, David S. Hollman and Michal Dominiak

slide-58
SLIDE 58

Type punning through unions

58

union Pun { int x; unsigned char c[sizeof(int)]; }; void bad(Pun& u) { u.x = 'x’; std::cout << u.c[0] << '\n'; // undefined behaviour }

slide-59
SLIDE 59

Type punning through unions

59

  • Popular in C; no alternative
  • Used on systems with limited capacity
  • (mis)Used for type punning
  • C++ has static_cast<>()
  • std::byte with static_cast<char>()

std::vector<std::byte> i_buffer; i_buffer.push_back(std::byte(0b01000011)); std::cout << static_cast<char>(i_buffer[0]) << '\n';

slide-60
SLIDE 60

Closing thoughts

60

slide-61
SLIDE 61

Many ways to do the same

61

slide-62
SLIDE 62

Hard to learn

62

slide-63
SLIDE 63

Hard to teach

63

slide-64
SLIDE 64

Versatile

64

slide-65
SLIDE 65

Fun

65

slide-66
SLIDE 66

This is why we can(‘t) have nice things

Timo van der Kuil

66

16/11/2019 Meeting C++