Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use...

59
Elements of Modern C++ Style Adapting to the New World Order

Transcript of Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use...

Page 1: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Elements of Modern C++ Style

Adapting to the New World Order

Page 2: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Winter is coming...

Warning: this talk assumes working knowledge of C++98/03

Page 3: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Winter is coming...

“C++11 feels like a new language. I write code differently now than I did in C++98. The C++11 code is shorter, simpler, and usually more efficient than what I used to write.”

Bjarne StroustrupInventor of C++

Page 4: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Winter is coming...

C++ is hard to use● operator overloading● template programming● multiple inheritance● pointers● STL

Page 5: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Winter is coming...

Unfamiliar things are hard to use● lambdas● type deduction● move semantics● smart pointers● memory model

Page 6: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Winter is coming...Not your grandfather’s C++

Client MantisServer::CreateClient(const std::string &clientid, const std::string &streamid, const std::string &connectionid) {

std::shared_ptr<Client> client;

if (requestqueue_) {

requestqueue_->push({[=, &client](mantis::core::Server *server) {

client = server->CreateClient(clientid, streamid, connectionid);

}});

}

Page 7: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Winter is coming...Mission: completely understand what this is doing

Client MantisServer::CreateClient(const std::string &clientid, const std::string &streamid, const std::string &connectionid) {

std::shared_ptr<Client> client;

if (requestqueue_) {

requestqueue_->push({[=, &client](mantis::core::Server *server) {

client = server->CreateClient(clientid, streamid, connectionid);

}});

}

Page 8: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

On upcoming standards

● C++14 is a minor update on 11○ fix errata○ fix holes/overlooked issues○ make features more generic

● C++17 is a major update on 11○ new std libraries for filesystem and networking○ new concurrency constructs○ optional and any types

Page 9: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

lambdastruct Object {

int value;

};

struct ObjectTooBigPredicate {

int max;

ObjectTooBigPredicate(int m) : max(m) {}

bool operator()(const &Object obj) { return obj.value > max; }

}

void foo(const std::vector<Object> &objs) {

std::vector<Object>::const_iterator i = std::find_if(objs.begin(), objs.end(),

ObjectTooBigPredicate(5));

// *i is TOO BIG (compared to 5)!

Page 10: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

lambdastruct Object {

int value;

};

void foo(const std::vector<Object> &objs) {

std::vector<Object>::const_iterator i = std::find_if(objs.begin(), objs.end(),

[](const Object &obj) {

return obj.value > ??;

});

// *i is TOO BIG!

Page 11: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

lambdastruct Object {

int value;

};

void foo(const std::vector<Object> &objs) {

const int max = 5;

std::vector<Object>::const_iterator i = std::find_if(objs.begin(), objs.end(),

[max](const Object &obj) {

return obj.value > max;

});

// *i is TOO BIG (compared to 5)!

Page 12: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

lambdastruct Object {

int value;

};

void foo(const std::vector<Object> &objs) {

size_t count = 0;

const int max = 5;

std::vector<Object>::const_iterator i = std::find_if(objs.begin(), objs.end(),

[max, &count](const Object &obj) {

if (obj.value > max) {

++count; return true;

}

return false;

});

log(“found: “ << count); // actually find_if doesn’t work this way

Page 13: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

lambdastruct Object {

int value;

};

void foo(const std::vector<Object> &objs) {

size_t count = 0;

const int max = 5;

std::vector<Object>::const_iterator i = std::find_if(objs.begin(), objs.end(),

[&](const Object &obj) {

if (obj.value > max) {

++count; return true;

}

return false;

});

log(“found: “ << count); // actually find_if doesn’t work this way

Page 14: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

lambdastruct Object {

int value;

};

void foo(const std::vector<Object> &objs) {

size_t count = 0;

const int max = 5;

std::function<bool(const Object &)> pred = [&](const Object &obj) {

if (obj.value > max) {

++count; return true;

}

return false;

}

std::vector<Object>::const_iterator i = std::find_if(objs.begin(), objs.end(), pred);

Page 15: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 1: Prefer lambda to bind

● std::bind is opaque to compiler optimizations● requires awkward placeholder notation● doesn’t do anything lambda can’t

Page 16: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 1: Prefer lambda to bind // turn a class method into a void function!

struct Foo {

void bar(int v) { log(“bar: ” + v); }

};

template<typename Function>

void baz(Function function) {

function();

}

//...

Foo foo;

baz([]{ foo.bar(5); }); // OK!

Page 17: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

type deductionThe compiler already knows these types

bool foo(const std::vector<Object> &objs) {

typename std::vector<Object>::const_iterator i = objs.begin();

typename std::vector<Object>::const_iterator e = objs.end();

for (; i != e; ++i)

if (i->bar() == 5)

return false;

return true;

}

Page 18: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

type deductionThe compiler already knows these types

auto foo(const std::vector<Object> &objs) {

auto i = begin(objs);

auto e = end(objs);

for (; i != e; ++i)

if (i->bar() == 5)

return false;

return true;

}

Page 19: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

type deductionOr better yet

auto foo(const std::vector<Object> &objs) {

for (auto obj : objs)

if (obj.bar() == 5)

return false;

return true;

}

Page 20: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

type deductionWorks well with STL and lambdas

auto predicate = [](const Object &obj) {

return obj.value > 5;

};

std::vector<Object> list = { a, b, c, d };

auto found = std::find_if(begin(list), end(list), pred);

if (found != end(list)) {

log(“found it!”);

}

Page 21: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 2: Prefer type deductionint foo();

//...

int main() {

size_t n = foo(); // DANGER

auto m = foo(); // OK

if (m) log(“it worked!”);

}

Page 22: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 2: Prefer type deductionObject foo();

//...

int main() {

size_t n = foo(); // ERROR

auto m = foo(); // OK

if (m) log(“it worked!”); // OK? conversion operator

}

Page 23: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 2: Prefer type deductionLooks good right?

void foo(const std::vector<Object> &objs) {

for (Object obj : objs) {

bar(obj);

}

}

Page 24: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 2: Prefer type deductionWhat if something changes?

void foo(const std::vector<Object *> &objs) {

for (Object obj : objs) { // ERROR

bar(obj);

}

}

Page 25: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 2: Prefer type deduction

void foo(const std::vector<Object *> &objs) {

for (auto obj : objs) { // LIFE IS SWEET

bar(obj); // depending on bar()

}

}

Page 26: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 2: Prefer type deduction

void foo(const std::vector<Object> &objs) {

for (const auto &obj : objs) {

bar(obj);

}

}

Page 27: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semantics“There is a couple of things that make C++ unique among other contemporary mainstream programming languages. One of these things is value semantics. Value semantics is the programming style, or the way of thinking, where we focus on values that are stored in the objects rather than objects themselves. The objects are only used to convey values: we do not care about object’s identity.”

Andrzej Krzemieński

Page 28: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semantics

● contrast with reference semantics○ needed with objects that have identity (actors)○ commonly found in object-oriented languages

● values are immutable and copied on assignment○ easy to reason about○ commonly found in functional languages

Page 29: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semanticseasy to reason about

int foo(int v) {

v += 6;

return v;

}

int a = 5;

int b = foo(a);

log(“a: “ << a << “ b: “ << b);

Page 30: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semanticseasy to reason about

int foo(int v) {

v += 6;

return v;

}

int a = 5;

int b = std::async(foo(a)).get();

log(“a: “ << a << “ b: “ << b);

Page 31: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semanticseasy to reason about

std::string foo(std::string v) {

v += “6”;

return v;

}

std::string a = “5”;

std::string b = foo(a);

// a == “5”, b == “56”

Page 32: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semanticseasy to reason about?

Object foo(Object obj) {

obj += 6;

return obj;

}

Object a = 5;

Object b = foo(a);

// a and b are independent values

Page 33: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semanticscopy constructor/assignment

Object::Object(const Object &copy) {

// copy over internal state.

}

Object &operator=(const Object &copy) {

// copy over internal state.

}

Page 34: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semanticseasy to reason about?

Object foo(Object &obj); // obj *is* a

Object a = 5;

Object b = foo(a);

// foo has side-effects on a

Page 35: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semanticseasy to reason about??

Object *foo(Object *obj); // obj *is* a

Object *a = new Object(5);

Object *b = foo(a); // who is b??

// who manages these lifetimes?

Page 36: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semantics● heap-based objects

○ have identity (memory address)○ cheap to move (pointer assignment)○ mutable by anyone who knows the address

● stack or register based objects○ no identity (stacks/registers go away)○ expensive to move (copy value)○ mutable only locally

Page 37: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

value semanticsHeap allocated data + Stack allocated owner● std::vector, std::string, etc.● std::shared_ptr● copy-on-write data structures

Page 38: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

C++03 Element: Prefer value semantics

Use stack-based objects to manage heap-based resources

Page 39: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticsHeap allocated data + Stack allocated owner● std::vector, std::string, etc.● std::shared_ptr● copy-on-write data structures

What about std::unqiue_ptr, or std::thread??

Page 40: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticswhat does this even mean?

Object obj; std::thread th1(foo(obj));

std::thread th2 = th1;

th2.join();

// have we spawned two threads?

// if so where is obj?

Page 41: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticswhat does this even mean?

std::unique_ptr<Object> ptr1(new Object());

std::unique_ptr<Object> ptr2(ptr1);

// who deletes Object?

Page 42: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticscan we make transfer of ownership explicit?

std::unique_ptr<Object> ptr1(new Object());

std::unique_ptr<Object> ptr2(std::move(ptr1));

// ptr2 deletes Object.

Page 43: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semantics

1. enforces unique ownership2. avoids unnecessary copies

Page 44: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticsmove constructor/assignment

Object::Object(Object &&move) {

// move over internal state.

}

Object &operator=(Object &&move) {

// move over internal state.

}

Page 45: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semantics

std::move is a function that casts T to T&&

Page 46: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticsavoid unnecessary copies

Object foo() {

Object bar; // default constructor

return bar;

}

Object obj(foo()); // copy in C++03

Page 47: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticsavoid unnecessary copies

Object foo() {

Object bar; // default constructor

return bar;

}

Object obj(foo()); // move in C++11

Page 48: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticsproblem: lots of new overloads!

Object::setBar(const Bar &bar); // copy read-only Bar

Object::setBar(Bar &&bar); // move temporary Bar

Page 49: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

move semanticssolution: new parameter passing idiom

Object::setBar(Bar bar) { // copied or moved

mybar_ = std::move(bar); // no copy

}

Page 50: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 3: Understand move semantics

● Libraries will do most of the work for you● Libraries will expose move-only objects● Unique-owner implies move-only semantics● Move operators can save you copies● Understand parameter passing idiom

Page 51: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

Element 3: Understand move semantics

Future APIs will look like

struct Foo {

void consume(Resource resource);

Resource produce();

void observe(const Object &object);

void share(std::shared_ptr<Object> actor);

void sink(std::unique_ptr<Object> slave);

};

Page 52: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

smart pointers“Always use the standard smart pointers, and non-owning raw pointers. Never use owning raw pointers and delete. Use shared_ptr to express shared ownership, and use unique_ptr to express unique ownership. Prefer std::make_shared or std::make_unique to construct shared or uniquely-owned objects.”

[Paraphrased]Herb Sutter

Scott Meyers

Page 53: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

smart pointers

● shared_ptr○ weak_ptr prevents cycles○ reference counted○ custom deleters○ thread-safe

● unique_ptr○ ownership semantics○ source/sink semantics○ automatic deletion

Page 54: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

smart pointersavoid direct use of new/delete operators

auto sh1 = std::make_shared<Object>(a,b,c,d);

std::shared_ptr<Object> sh2 = sh1;

std::thread th(foo(sh2)); th.detach();

sh1.reset(); // sh1 no longer shares object

assert(sh1 == false);

sh2.reset(); // sh2 no longer shares object

// object will delete when thread completes

Page 55: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

smart pointersweak_ptr

auto shared = std::make_shared<Object>(a,b,c,d);

std::weak_ptr<Object> weak = shared;

//...

auto upgraded = weak.lock();

if (!upgraded) log(“shared data expired!”);

Page 56: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

smart pointersraw pointers can still be used to alias buffers

uint8_t buf[N];

auto p1 = &buf[i];

auto p2 = &buf[j];

swap(*p1, *p2);

Page 57: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

memory modelSorry, this is a whole other talk!

Page 58: Style Elements of Modern C+++-Style.pdf · Winter is coming... Unfamiliar things are hard to use lambdas type deduction move semantics smart pointers memory model

conclusionCan anyone tell me what this does now?

Client MantisServer::CreateClient(const std::string &clientid, const std::string &streamid, const std::string &connectionid) {

std::shared_ptr<Client> client;

if (requestqueue_) {

requestqueue_->push({[=, &client](mantis::core::Server *server) {

client = server->CreateClient(clientid, streamid, connectionid);

}});

}