63
c 2012– Andrei Alexandrescu. 1 / 59 Systems Programming & Beyond using C++ and D C++ Andrei Alexandrescu [email protected] Prepared for LASER Summer School 2012

Systems Programming & Beyond using C++ and D C++laser.inf.ethz.ch/2012/slides/Alexandrescu/1-C++ course...c 2012– Andrei Alexandrescu. 1 / 59 Systems Programming & Beyond using C++

Embed Size (px)

Citation preview

c© 2012– Andrei Alexandrescu. 1 / 59

Systems Programming & Beyond using C++ and D

C++

Andrei Alexandrescu

[email protected]

Prepared for LASER Summer School 2012

Prolegomena

c© 2012– Andrei Alexandrescu. 3 / 59

• Assumption: You are intelligent and talented

• Won’t teach trivialities

• Dwell on “diffs”

• Focus on internalizing simple fundamentals with broad

impact instead of rote memorization

◦ Only few errors are unforced in PL design

Rules of Engagement

c© 2012– Andrei Alexandrescu. 4 / 59

• I ask question:

◦ You raise hand

◦ I acknowledge

◦ You SHOUT

• You ask question:

◦ You raise hand

◦ I ignore, microphone runners acknowledge

◦ You raise hand with microphone in it

◦ I acknowledge

◦ You talk

• No two consecutive questions

Why?

c© 2012– Andrei Alexandrescu. 5 / 59

c© 2012– Andrei Alexandrescu. 6 / 59

“OK, efficiency is the word. But then why

wouldn’t I just use C or C + glue?”

C

c© 2012– Andrei Alexandrescu. 7 / 59

• + Fast

• + Close to the machine

• − Weak abstraction mechanism

• − No safety

• − Verbose

C + glue

c© 2012– Andrei Alexandrescu. 8 / 59

• + Flexibility

• + Separation of concerns

• + Get to use another language

• − Hybrid object model and memory layout

• − Impedance mismatch

• − Get to use another language

C++

c© 2012– Andrei Alexandrescu. 9 / 59

• + Perfect integration

• + Better abstraction mechanisms than C

• + Expressiveness, range

• − Lack of flexibility

• − Size

• − Imperfections (e.g. safety)

C++’s Computational Model

c© 2012– Andrei Alexandrescu. 10 / 59

C++

c© 2012– Andrei Alexandrescu. 11 / 59

• Part of many battles, most of which it won

• Retrofitted with armor and weaponry, sent back to

battle

• Amazing combination of old technology and new

human ingenuity

• Aimed niche: efficiency AND modeling power

◦ All else is secondary

Example

c© 2012– Andrei Alexandrescu. 12 / 59

#include <iostream>

int main() {

std::cout << "Hello, world!" << std::endl;

}

• Incorrect

• Inefficient

• Relies on one exceedingly subtle language rule to work

• Surreptitiously uses inversion of control

• Works due to an exception disallowed everywhere else

Memory model

c© 2012– Andrei Alexandrescu. 13 / 59

• Memory bedrock inherited largely from C

• You know where most everything is

• Exceptions:

◦ Pointer to vtable

◦ Pointer to parent class in virtual inheritance

schemes

◦ Address of constructor/destructor

◦ Offsets of members in some classes

• Impacts:

◦ Inherent efficiency

◦ Approach to safety

Underlying Machine

c© 2012– Andrei Alexandrescu. 14 / 59

• Most code predictably translates into either:

◦ Simple machine code instructions

◦ Direct function calls

◦ Indirect function calls

◦ That’s pretty much it!

• Exceptions:

◦ Constructor/destructor calls

◦ Exception throwing, transporting, catching

Constructor and destructor calls

c© 2012– Andrei Alexandrescu. 15 / 59

• Ctor calls + copy ctor calls == dtor calls

• No ownership transfer semantics (C++11 fixes that)

• Invariant is difficult to maintain

◦ Very often spurious calls are present

◦ Benchmark: 1-7 ctor calls for the same code

depending on compiler and optimization level

• Geared toward value semantics, not polymorphism

You don’t pay for what you don’t use

c© 2012– Andrei Alexandrescu. 16 / 59

• RTTI/dynamic_cast adds per-class overhead even

when never used.

• Exceptions have non-zero costs even when never used

◦ Impossible to specify “nothrow” modularly

• Even C functions are assumed to throw!

◦ All exceptions are catchable

◦ Complicates most function frames

Building Abstraction

c© 2012– Andrei Alexandrescu. 17 / 59

What is abstraction?

c© 2012– Andrei Alexandrescu. 18 / 59

What is abstraction?

c© 2012– Andrei Alexandrescu. 18 / 59

Selective ignorance

Why is selective ignorance good?

c© 2012– Andrei Alexandrescu. 19 / 59

Why is selective ignorance good?

c© 2012– Andrei Alexandrescu. 19 / 59

Allows you to ignore details

Why is ignoring details good?

c© 2012– Andrei Alexandrescu. 20 / 59

Why is ignoring details good?

c© 2012– Andrei Alexandrescu. 20 / 59

MODULARITY

Surprising fact #1

c© 2012– Andrei Alexandrescu. 21 / 59

Most software engineering tools and

techniques ultimately aim at improving

modularity

Modularity

c© 2012– Andrei Alexandrescu. 22 / 59

• Type systems

• Functions

• Information hiding

• Encapsulation

• Objects

• Design patterns

• Lazy evaluation

• Lambdas

• Monads

• Memcache!

• . . .

Modularity through abstraction

c© 2012– Andrei Alexandrescu. 23 / 59

• Abstraction is a powerful modularity mechanism

• Power in nomenclature

• Stable: ‖ abstractions ‖ < ‖ details ‖• Allows keeping details in separation

• Allows improvement of details in separation

Abstraction liabilities

c© 2012– Andrei Alexandrescu. 24 / 59

• Efficiency cost (“abstraction friction”)

• Cognitive cost

• Verbosity cost

• Informational cost

• Leakiness

C++ functions as abstraction mechanism

c© 2012– Andrei Alexandrescu. 25 / 59

• Low barrier of entry, starting at $0

• Eager evaluation

• Genericity (templates)

• Specialization (template specialization)

• Ambiguous input and output parameters

• Uninformative signatures

◦ No escaping information

◦ No purity information

◦ No safety information

◦ No exception information

◦ People use convention for all of the above

Example: find item in array, work with it

c© 2012– Andrei Alexandrescu. 26 / 59

vector<int> v;

int e;

...

for (int i = 0; i < v.size(); ++i) {

if (v[i] == e) {

... /* do work */ ...

break;

}

}

• Relies on vector

• Mixes searching details with work details

• Difficult to improve modularly

Example: find item in array, work with it

c© 2012– Andrei Alexandrescu. 27 / 59

vector<int> v;

int e;

...

auto i = find(v.begin(), v.end(), e);

if (i != v.end()) {

... /* do work */ ...

}

• Works with many topologies

• Searching details separated from work details

• Can be improve modularly

find can be improved in isolation

c© 2012– Andrei Alexandrescu. 28 / 59

template <class RIt, class V>

RIt find(RIt b, RIt e, const V& v) {

for (; (e - b) & 3; ++b) {

if (*b == v) return b;

}

for (; b != e; b += 4) {

if (*b == v) return b;

if (b[1] == v) return b + 1;

if (b[2] == v) return b + 2;

if (b[3] == v) return b + 3;

}

}

Good functional abstractions

c© 2012– Andrei Alexandrescu. 29 / 59

• Address frequent (ENFORCE) or specific

(multiwayUnion) intent

• Sound-bite name (verb); easy to talk about

• Express inputs, outputs, lifetime, and ownership

• Hide operational details; easy to describe in one

sentence

• Generally applicable; do not commit to irrelevant details

(find)

• Do not treat complexity as a detail

Building abstractions with

objects

c© 2012– Andrei Alexandrescu. 30 / 59

C++ classes

c© 2012– Andrei Alexandrescu. 31 / 59

• Class is the unit of encapsulation in C++

• Access control applies at class level

• Methods and friend functions have access to everything

inside the class

◦ Not the base class

• Key point: multiple instances of the class

• Key point: information hiding through data

encapsulation

Class methods vs. free functions

c© 2012– Andrei Alexandrescu. 32 / 59

• Conventional wisdom: methods are cool, free functions

are so 1960s

• Yet:

◦ Free functions improve encapsulation over methods

◦ Free functions may be more general

◦ Free functions decouple better

◦ Free functions support conversions on their

left-hand argument

Surprising fact #2

c© 2012– Andrei Alexandrescu. 33 / 59

Making a function a method should be

your last, not first, choice

c© 2012– Andrei Alexandrescu. 34 / 59

Value vs. reference semantics

c© 2012– Andrei Alexandrescu. 35 / 59

• Per discussion in Part 1:

◦ Equal copies algebraically equivalent (think int)

◦ Polymorphic values have “personality”—one

instance, many referents

• Always decide early on value vs. reference

• Never define ambiguous-gender types

◦ Exception: exceptions

Archetypal value

c© 2012– Andrei Alexandrescu. 36 / 59

class V {

public:

V(); // nothrow

V(const V & rhs); // deep copy

~V();

V& operator=(const V& rhs);

...

};

Archetypal reference

c© 2012– Andrei Alexandrescu. 37 / 59

class R {

R& operator=(const R& rhs); // not defined

protected:

R(const R & rhs); // optional; deep copy

public:

R(); // optional

virtual ~R(); // essential

virtual unique_ptr<R> clone() {

CHECK(typeid(*this) == typeid(R));

return new R(*this);

}

...

};

The C++ Standard Library

c© 2012– Andrei Alexandrescu. 38 / 59

The C++ Standard Library

c© 2012– Andrei Alexandrescu. 39 / 59

• “The reptilian brain:” The C Standard Library

• “Limbic system:” iostreams, string, complex, . . .

• “Neocortex:” The Standard Template Library

The STL

c© 2012– Andrei Alexandrescu. 40 / 59

• Arguably the best software library ever conceived

• Focuses on asking the right question

• “What is the essence of the fundamental algorithms

and containers?”

◦ Arguably most everything else is aftermath

• Abstract: Leaving room for redesign is fail

• Frictionless: Leaving incentive for reimplementation is

fail

Bottleneck design

c© 2012– Andrei Alexandrescu. 41 / 59

• Many algorithms, no categorization

• Few iterators, strict categorization

◦ input

◦ output

◦ forward

◦ bidirectional

◦ random access

• Many containers, loose categorization

STL algorithms

c© 2012– Andrei Alexandrescu. 42 / 59

• Many fundamental algorithms

◦ sorting

◦ searching

◦ accumulation

◦ transformation

• Never operate directly on containers

• Never affect topology, only data

STL containers

c© 2012– Andrei Alexandrescu. 43 / 59

• No hierarchy

• Federation of loosely categorized objects

• Linear: vector, list, deque

• Associative: set, map, unordered map

• Choice is by complexity/validity guarantees

The star of STL: iterators

c© 2012– Andrei Alexandrescu. 44 / 59

• Mediate traffic between algorithms and containers

• Generalization of pointers

• Input: i == j, *i, ++i

• Forward: Input, i = j

• Bidirectional: Forward, --i

• Random access: Bidirectional, i[n], i + n

What do you need for. . .

c© 2012– Andrei Alexandrescu. 45 / 59

• Linear search

• Subsequence brute force search

• Boyer-Moore search

• Quicksort

• Bring-to-front

• Stable remove

• Unstable remove

STL assets

c© 2012– Andrei Alexandrescu. 46 / 59

• Abstraction power

• Efficiency

• Offers a model for other algorithms, containers, and

iterators

• Plays perfectly into C++’s strengths

◦ Most other languages cannot implement STL

◦ This is remarkable

STL liabilities

c© 2012– Andrei Alexandrescu. 47 / 59

• Lack of lambda functions makes many algorithms

tenuous

◦ Fixed in C++11

• Allocator design inadequate

• Syntax relatively verbose

• “C++ complete”

◦ Ethos impossible to understand outside of C++

Practical advice

c© 2012– Andrei Alexandrescu. 48 / 59

• Prefer STL to ad-hoc containers

◦ Prefer our containers to the default ones

• Prefer iterators to indexed access

• Understand, use, and extend algorithms

• Avoid dogma beyond all else

Generic Programming

c© 2012– Andrei Alexandrescu. 49 / 59

What is generic programming?

c© 2012– Andrei Alexandrescu. 50 / 59

What is generic programming?

c© 2012– Andrei Alexandrescu. 50 / 59

Generic Programming is the endeavor of finding the most

abstract expression of a computation without losing its

essence or efficiency.

Symptoms of generic programming deficit

c© 2012– Andrei Alexandrescu. 51 / 59

• “Same drama, different stage”

◦ Implement same algorithm several times

• Design with high-level notions but implement from first

principles

• Can’t reuse a function without changing a few details

• Similar methods in unrelated hierarchies

C++ for generic programming?

c© 2012– Andrei Alexandrescu. 52 / 59

• + Template engine abstracts at no/low cost

• + Post-hoc specialization possible

• + Good efficiency to begin with

• + No commitment to one paradigm

• − Ad-hoc features, no unifying arc

• − Uses vast array of odd features

Example: specializing on iterator

c© 2012– Andrei Alexandrescu. 53 / 59

• Recall from earlier:

template <class RIt, class V>

RIt find(RIt b, RIt e, const V& v) {

for (; (e - b) & 3; ++b) {

if (*b == v) return b;

}

for (; b != e; b += 4) {

if (*b == v) return b;

if (b[1] == v) return b + 1;

if (b[2] == v) return b + 2;

if (b[3] == v) return b + 3;

}

}

• How do we specialize find only for random-access

iterators?

Dispatching on category

c© 2012– Andrei Alexandrescu. 54 / 59

• Classic technique: “tag dispatching”

• Not so classic: dispatch is static

• Now defining the base version working for all iterators

template <class It, class V>

It find(It b, It e, const V& v,

std::input_iterator_tag) {

for (; e != b; ++b) {

if (*b == v) return b;

}

}

Dispatching on category

c© 2012– Andrei Alexandrescu. 55 / 59

• Now defining the specialized version

template <class It, class V>

It find(It b, It e, const V& v,

std::random_access_iterator_tag) {

for (; (e - b) & 3; ++b) {

if (*b == v) return b;

}

for (; b != e; b += 4) {

if (*b == v) return b;

if (b[1] == v) return b + 1;

if (b[2] == v) return b + 2;

if (b[3] == v) return b + 3;

}

}

The Dispatcher

c© 2012– Andrei Alexandrescu. 56 / 59

template <class It, class V>

It find(It b, It e, const V& v) {

return find(b, e, v,

typename iterator_traits<It>::iterator_category());

}

Testing 1-2-3

c© 2012– Andrei Alexandrescu. 57 / 59

int main() {

vector<int> vec;

list<int> lst;

find(vec.begin(), vec.end(), 42);

find(lst.begin(), lst.end(), 42);

}

We’re about done!

• C++

• Prolegomena

• Rules of Engagement

Why?

C++’s Computational

Model

Building Abstraction

Building abstractions

with objects

The C++ Standard

Library

Generic Programming

We’re about done!

c© 2012– Andrei Alexandrescu. 58 / 59

c© 2012– Andrei Alexandrescu. 59 / 59

Questions ?