Presentation is loading. Please wait.

Presentation is loading. Please wait.

Aerix consulting C++11 Library Design Lessons from Boost and the Standard Library.

Similar presentations


Presentation on theme: "Aerix consulting C++11 Library Design Lessons from Boost and the Standard Library."— Presentation transcript:

1 aerix consulting C++11 Library Design Lessons from Boost and the Standard Library

2 aerix consulting Goals of This Talk 2 Copyright 2013 Aerix Consulting C++11 Gotchas!

3 aerix consulting Goals of This Talk 3 Copyright 2013 Aerix Consulting Tips and Tricks!

4 aerix consulting Goals of This Talk 4 Copyright 2013 Aerix Consulting Useless and Unnecessary TMP Heroics!

5 aerix consulting Goals of This Talk 5 Copyright 2013 Aerix Consulting Interface Design Best Practices

6 aerix consulting Talk Overview I. Function Interface Design II. Class Design III. “Module” Design 6 Copyright 2013 Aerix Consulting

7 aerix consulting Many Things Are The Same Classes  Should aim to be “Regular”  RAII for resource management  Throw from failed constructors  Don’t throw from destructors, etc. Functions:  Use * and & for non-owned resources 7 Copyright 2013 Aerix Consulting

8 aerix consulting All Wisdom Can Be Found On StackOverflow “The new additions […] actually has [sic] us all taking steps back to readjust and do things in a simpler way again -- more like when we started coding! Perhaps for too long we’ve all been optimizing to effectively bypass the compiler. With C++11 this is no longer needed as well-thought out/simpler code yields better/optimal performance just by letting the compiler do what it does. So with C++11, I try to remind myself to resist temptation to optimize but to rethink more simply.” – Paul Preney Aug 5 '12 at 18:44 8 Copyright 2013 Aerix Consulting

9 aerix consulting Copyright 2013 Aerix Consulting 9 I. Function Interface Design

10 aerix consulting “Is my function … ?” … easy to call correctly? … hard to call incorrectly? … efficient to call? …with minimal copying? …with minimal aliasing? …without unnecessary resource allocation? … easily composable with other functions? … usable in higher-order constructs? 10 Copyright 2013 Aerix Consulting

11 aerix consulting Function Interfaces What’s the best way of getting data into and out of a function? 11 Copyright 2013 Aerix Consulting

12 aerix consulting Passing and Returning in C++98 CategoryC++98 Recommendation Input smallPass by value largePass by const ref Output smallReturn by value largePass by (non-const) ref Input/OutputPass by non-const ref 12 Copyright 2013 Aerix Consulting How does C++11 change this picture?

13 aerix consulting Copyright 2013 Aerix Consulting 13

14 aerix consulting Input Argument Categories Read-only: value is only ever read from, never modified or stored Sink: value is consumed, stored, or mutated locally 14 Copyright 2013 Aerix Consulting struct TaskQueue { void Enqueue(Task const &); }; struct TaskQueue { void Enqueue(Task const &); }; Task saved somewhere std::ostream& operator<<(std::ostream&, Task const &); Task only read from

15 aerix consulting Input Argument Categories Read-only: value is only ever read from, never modified or stored 15 Copyright 2013 Aerix Consulting std::ostream& operator<<(std::ostream&, Task const &); Guideline 1: Continue taking read-only value by const ref (except small ones)

16 aerix consulting “Sink” Input Arguments, Take 1 Goal: Avoid unnecessary copies, allow temporaries to be moved in. 16 Copyright 2013 Aerix Consulting Task MakeTask(); Task t; TaskQueue q; q.Enqueue(t); // copies q.Enqueue(MakeTask()); // moves Task MakeTask(); Task t; TaskQueue q; q.Enqueue(t); // copies q.Enqueue(MakeTask()); // moves struct TaskQueue { void Enqueue(Task const &); void Enqueue(Task &&); }; struct TaskQueue { void Enqueue(Task const &); void Enqueue(Task &&); }; Handles lvalues Handles rvalues

17 aerix consulting Programmer Heaven? What if the function takes more than 1 sink argument? 17 Copyright 2013 Aerix Consulting struct TaskQueue { void Enqueue(Task const &, Task const &); }; struct TaskQueue { void Enqueue(Task const &, Task const &); }; struct TaskQueue { void Enqueue(Task const &, Task const &); void Enqueue(Task const &, Task &&); void Enqueue(Task &&, Task const &); void Enqueue(Task &&, Task &&); }; struct TaskQueue { void Enqueue(Task const &, Task const &); void Enqueue(Task const &, Task &&); void Enqueue(Task &&, Task const &); void Enqueue(Task &&, Task &&); }; “This isn’t heaven. This sucks.”

18 aerix consulting Sink Input Arguments, Take 2 Guideline 2: Take sink arguments by value 18 Copyright 2013 Aerix Consulting Task MakeTask(); Task t; TaskQueue q; q.Enqueue(t); // copies q.Enqueue(MakeTask()); // moves Task MakeTask(); Task t; TaskQueue q; q.Enqueue(t); // copies q.Enqueue(MakeTask()); // moves struct TaskQueue { void Enqueue(Task const &); void Enqueue(Task &&); }; struct TaskQueue { void Enqueue(Task const &); void Enqueue(Task &&); }; struct TaskQueue { void Enqueue(Task); }; struct TaskQueue { void Enqueue(Task); };

19 aerix consulting Passing and Returning in C++11 CategoryC++11 Recommendation Input small & “sink”Pass by value all othersPass by const ref OutputReturn by value Input/OutputPass by non-const ref (?) 19 Copyright 2013 Aerix Consulting

20 aerix consulting Example: getline std::istream & getline(std::istream &, std::string &); 20 Copyright 2013 Aerix Consulting Huh, why doesn’t getline return a line?

21 aerix consulting std::string line; if(std::getline(std::cin, line)) use_line(line); std::string line; if(std::getline(std::cin, line)) use_line(line); Example: getline std::istream & getline(std::istream &, std::string &); 21 Copyright 2013 Aerix Consulting Must declare a string on a separate line Can’t immediately use the result

22 aerix consulting std::string line; if(std::getline(std::cin, line)) use_line(line); std::string line; if(std::getline(std::cin, line)) use_line(line); Example: getline, Improved? std::istream & getline(std::istream &, std::string &); 22 Copyright 2013 Aerix Consulting // Isn’t this nicer? use_line(getline(std::cin)); // Isn’t this nicer? use_line(getline(std::cin)); std::string getline(std::istream &);

23 aerix consulting Example: getline int main() { std::string line; while(std::getline(std::cin, line)) { use_line(line); } int main() { std::string line; while(std::getline(std::cin, line)) { use_line(line); } 23 Copyright 2013 Aerix Consulting Repeated calls to getline should reuse memory! std::istream & getline(std::istream &, std::string &);

24 aerix consulting std::istream & getline(std::istream &, std::string &); getline: Observation 24 Copyright 2013 Aerix Consulting This is NOT an out parameter!

25 aerix consulting lines_range getlines(std::istream &); Example: getline for C++11 25 Copyright 2013 Aerix Consulting Fetches lines lazily, on demand “Out Parameters, Move Semantics, and Stateful Algorithms” “Out Parameters, Move Semantics, and Stateful Algorithms” http://ericniebler.com/2013/10/13/out-parameters-vs-move-semantics/ std::string data member gets reused for(std::string const& line : getlines(std::cin)) use_line(line); for(std::string const& line : getlines(std::cin)) use_line(line);

26 aerix consulting Input / Output Parameters They indicate an algorithm is stateful  E.g. current state, cache, precomputed data, buffers, etc. Guideline 3: Encapsulate an algorithm’s state in an object that implements the algorithm. Examples: lines_range, Boost’s boyer_moore 26 Copyright 2013 Aerix Consulting

27 aerix consulting Passing and Returning in C++11 CategoryC++11 Recommendation Input small & “sink”Pass by value all othersPass by const ref OutputReturn by value Input/OutputUse a stateful algorithm object (*) 27 Copyright 2013 Aerix Consulting (*) Initial state is a sink argument to the constructor

28 aerix consulting Copyright 2013 Aerix Consulting 28 Whither &&

29 aerix consulting OK, One Gotcha! 29 Copyright 2013 Aerix Consulting template void Enqueue( Queue & q, Task const & t ) { q.Enqueue( t ); } template void Enqueue( Queue & q, Task && t ) { q.Enqueue( std::move( t ) ); } template void Enqueue( Queue & q, Task const & t ) { q.Enqueue( t ); } template void Enqueue( Queue & q, Task && t ) { q.Enqueue( std::move( t ) ); } TaskQueue q; Task t = MakeTask(); Enqueue( q, t ); TaskQueue q; Task t = MakeTask(); Enqueue( q, t ); If you don’t know why this code is broken, seriously reconsider trying to do something clever with rvalue references! Const ref here Rvalue ref here Which overload?

30 aerix consulting Copyright 2013 Aerix Consulting 30 “Fear rvalue refs like one might fear God. They are powerful and good, but the fewer demands placed on them, the better.” — Me

31 aerix consulting Perfect Forwarding Pattern Uses [variadic] templates and rvalue refs in a specific pattern: 31 Copyright 2013 Aerix Consulting template auto invoke( Fun && fun, Args &&... args ) -> ??? { return std::forward (fun)(std::forward (args)...); } template auto invoke( Fun && fun, Args &&... args ) -> ??? { return std::forward (fun)(std::forward (args)...); } template auto invoke( Fun && fun, Args &&... args ) -> decltype( the-mess-below ) { return std::forward (fun)(std::forward (args)...); } template auto invoke( Fun && fun, Args &&... args ) -> decltype( the-mess-below ) { return std::forward (fun)(std::forward (args)...); } template auto invoke( Fun && fun, Args &&... args ) -> decltype( auto ) { return std::forward (fun)(std::forward (args)...); } template auto invoke( Fun && fun, Args &&... args ) -> decltype( auto ) { return std::forward (fun)(std::forward (args)...); } template auto invoke( Fun && fun, Args &&... args ) { return std::forward (fun)(std::forward (args)...); } template auto invoke( Fun && fun, Args &&... args ) { return std::forward (fun)(std::forward (args)...); } C++14 only Argument is of form T&& where T is deduced Argument is used with std::forward (t)

32 aerix consulting Copyright 2013 Aerix Consulting 32 II. Class design Designing classes for C++11

33 aerix consulting Class Design in C++11 How to design a class in C++11…  … that makes best use of C++11  … that plays well with C++11 language features  Copy, assign, move, range-based for, etc.  Composes well with other types  Can be used anywhere (heap, stack, static storage, in constant expressions, etc.) library features  Well-behaved in generic algorithms  Well-behaved in containers 33 Copyright 2013 Aerix Consulting

34 aerix consulting “Can my type be…?” …copied and assigned? …efficiently passed and returned? …efficiently inserted into a vector? …sorted? …used in a map? An unordered_map? …iterated over (if it’s a collection)? …streamed? …used to declare global constants? 34 Copyright 2013 Aerix Consulting

35 aerix consulting Regular Types What are they?  Basically, int -like types.  Copyable, default constructable, assignable, equality-comparable, swappable, order-able Why do we care?  They let us reason mathematically  The STL containers and algorithms assume regularity in many places How do they differ in C++03 and C++11? 35 Copyright 2013 Aerix Consulting

36 aerix consulting C++98 Regular Type class Regular { Regular(); Regular(Regular const &); ~Regular(); // throw() Regular & operator=(Regular const &); friend bool operator==(Regular const &, Regular const &); friend bool operator!=(Regular const &, Regular const &); friend bool operator<(Regular const &, Regular const &); friend void swap(Regular &, Regular &); // throw() }; class Regular { Regular(); Regular(Regular const &); ~Regular(); // throw() Regular & operator=(Regular const &); friend bool operator==(Regular const &, Regular const &); friend bool operator!=(Regular const &, Regular const &); friend bool operator<(Regular const &, Regular const &); friend void swap(Regular &, Regular &); // throw() }; 36 Copyright 2013 Aerix Consulting T a = b; assert(a==b); T a; a = b;  T a = b; T a = c; T b = c; a = d; assert(b==c); T a = c; T b = c; zap(a); assert(b==c && a!=b); Or specialize std::less “Fundamentals of Generic Programming”, J. Dehnert, A. Stepanov, http://www.stepanovpapers.com/DeSt98.pdf http://www.stepanovpapers.com/DeSt98.pdf

37 aerix consulting C++11 Regular Type class RegularCxx11 { RegularCxx11(); RegularCxx11(RegularCxx11 const &); RegularCxx11(RegularCxx11 &&) noexcept; ~RegularCxx11(); RegularCxx11 & operator=(RegularCxx11 const &); RegularCxx11 & operator=(RegularCxx11 &&) noexcept; friend bool operator==(RegularCxx11 const &, RegularCxx11 const &); friend bool operator!=(RegularCxx11 const &, RegularCxx11 const &); friend bool operator<(RegularCxx11 const &, RegularCxx11 const &); friend void swap(RegularCxx11 &, RegularCxx11 &); // throw() }; namespace std { template<> struct hash ; } class RegularCxx11 { RegularCxx11(); RegularCxx11(RegularCxx11 const &); RegularCxx11(RegularCxx11 &&) noexcept; ~RegularCxx11(); RegularCxx11 & operator=(RegularCxx11 const &); RegularCxx11 & operator=(RegularCxx11 &&) noexcept; friend bool operator==(RegularCxx11 const &, RegularCxx11 const &); friend bool operator!=(RegularCxx11 const &, RegularCxx11 const &); friend bool operator<(RegularCxx11 const &, RegularCxx11 const &); friend void swap(RegularCxx11 &, RegularCxx11 &); // throw() }; namespace std { template<> struct hash ; } 37 Copyright 2013 Aerix Consulting “What is a ‘Regular Type’ in the context of move semantics?” S. Parent, stackoverflow.com, Dec 2012 http://stackoverflow.com/a/14000046/195873http://stackoverflow.com/a/14000046/195873 RegularCxx11(RegularCxx11 &&) noexcept; RegularCxx11 & operator=(RegularCxx11 &&) noexcept;

38 aerix consulting C++11 Class Design Guideline 4: Make your types regular (if possible) Guideline 5: Make your types’ move operations noexcept (if possible) 38 Copyright 2013 Aerix Consulting

39 aerix consulting Implicitly Generated Move Ctor If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if X does not have a user-declared copy constructor, X does not have a user-declared copy assignment operator, X does not have a user-declared move assignment operator, X does not have a user-declared destructor, and the move constructor would not be implicitly defined as deleted. [ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that otherwise would have invoked the move constructor may instead invoke a copy constructor. —end note ] N3291 §12.8/9 39 Copyright 2013 Aerix Consulting

40 aerix consulting Implicitly Generated Move = If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if X does not have a user-declared copy constructor, X does not have a user-declared move constructor, X does not have a user-declared copy assignment operator, X does not have a user-declared destructor, and the move assignment operator would not be implicitly defined as deleted. N3291 §12.8/9 40 Copyright 2013 Aerix Consulting

41 aerix consulting Statically Check Your Classes Q: Is my type Regular? A: Check it at compile time! 41 Copyright 2013 Aerix Consulting template struct is_regular : std::integral_constant< bool, std::is_default_constructible ::value && std::is_copy_constructible ::value && std::is_move_constructible ::value && std::is_copy_assignable ::value && std::is_move_assignable ::value > {}; template struct is_regular : std::integral_constant< bool, std::is_default_constructible ::value && std::is_copy_constructible ::value && std::is_move_constructible ::value && std::is_copy_assignable ::value && std::is_move_assignable ::value > {}; struct T {}; static_assert(is_regular ::value, "huh?"); struct T {}; static_assert(is_regular ::value, "huh?");

42 aerix consulting equality_comparable namespace detail { template std::false_type check_equality_comparable(T const & t, long); template auto check_equality_comparable(T const & t, int) -> typename std::is_convertible ::type; } template struct is_equality_comparable : decltype(detail::check_equality_comparable(std::declval (), 1)) {}; namespace detail { template std::false_type check_equality_comparable(T const & t, long); template auto check_equality_comparable(T const & t, int) -> typename std::is_convertible ::type; } template struct is_equality_comparable : decltype(detail::check_equality_comparable(std::declval (), 1)) {}; 42 Copyright 2013 Aerix Consulting t == t

43 aerix consulting A Very Moving Example Imagine a unique_ptr that guarantees its pointer is non-null: 43 Copyright 2013 Aerix Consulting template class non_null_unique_ptr { T* ptr_; public: non_null_unique_ptr() : ptr_(new T{}) {} non_null_unique_ptr(T* p) : ptr_(p) { assert(p); } T* get() const { return ptr_; } non_null_unique_ptr(non_null_unique_ptr &&) noexcept; // ??? // etc... }; template class non_null_unique_ptr { T* ptr_; public: non_null_unique_ptr() : ptr_(new T{}) {} non_null_unique_ptr(T* p) : ptr_(p) { assert(p); } T* get() const { return ptr_; } non_null_unique_ptr(non_null_unique_ptr &&) noexcept; // ??? // etc... }; What does non_null_unique_ptr ’s move c’tor do?

44 aerix consulting A Very Moving Example Class invariant of non_null_unique_ptr: ptr.get() != nullptr What does the move c’tor do? 44 Copyright 2013 Aerix Consulting // Move constructor non_null_unique_ptr(non_null_unique_ptr&& other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; } // Move constructor non_null_unique_ptr(non_null_unique_ptr&& other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; } Is this OK???

45 aerix consulting A Very Moving Example Consider this code: 45 Copyright 2013 Aerix Consulting non_null_unique_ptr pint{ new int(42) }; non_null_unique_ptr pint2{ std::move( pint ) }; assert(pint.get() != nullptr); // assert the class invariant. non_null_unique_ptr pint{ new int(42) }; non_null_unique_ptr pint2{ std::move( pint ) }; assert(pint.get() != nullptr); // assert the class invariant.

46 aerix consulting A Very Moving Example Moved-from objects must be in a valid but unspecified state 46 Copyright 2013 Aerix Consulting

47 aerix consulting A Very Moving Example Q: Is this a better move constructor? 47 Copyright 2013 Aerix Consulting non_null_unique_ptr(non_null_unique_ptr&& other) : ptr_(new T(*other.ptr_)) { std::swap(ptr_, other.ptr_); } non_null_unique_ptr(non_null_unique_ptr&& other) : ptr_(new T(*other.ptr_)) { std::swap(ptr_, other.ptr_); } A: No:  It’s no different than a copy constructor!  It can’t be noexcept (non-ideal, but not a deal-breaker, per se)

48 aerix consulting A Very Moving Conclusion Either: 1. non_null_unique_ptr doesn’t have a natural move constructor, or 2. non_null_unique_ptr just doesn’t make any sense. 48 Copyright 2013 Aerix Consulting

49 aerix consulting Movable Types Summary Guideline 6: The moved-from state must be part of a class’s invariant. Guideline 7: If Guideline 6 doesn’t make sense, the type isn’t movable. Corollary: Every movable type must have a cheap(er)-to-construct, valid default state. 49 Copyright 2013 Aerix Consulting Further discussion can be found here: http://lists.boost.org/Archives/boost/2013/01/200057.php http://lists.boost.org/Archives/boost/2013/01/200057.php

50 aerix consulting Copyright 2013 Aerix Consulting 50 III. Modules Library Design in the Large

51 aerix consulting Good Modules: Good and Bad Bad Copyright 2013 Aerix Consulting 51

52 aerix consulting Large-Scale C++11 In C++11, what support is there for…  … enforcing acyclic, hierarchical physical component dependencies?  … decomposing large components into smaller ones?  … achieving extensibility of components?  … versioning (source & binary) components? 52 Copyright 2013 Aerix Consulting

53 aerix consulting Large-scale C++11:The Bad News No proper modules support No support for dynamically loaded libraries No explicit support for interface or implementation versioning …so no solution for the fragile base class problem 53 Copyright 2013 Aerix Consulting

54 aerix consulting Evolving A Library New library version with interface-breaking changes 54 Copyright 2013 Aerix Consulting namespace lib { struct foo { /*...*/ }; void bar(foo); template struct traits { /*...*/ }; } namespace lib { struct foo { /*...*/ }; void bar(foo); template struct traits { /*...*/ }; } namespace lib { struct base { virtual ~base() {} }; struct foo : base { /*...*/ }; int bar(foo, int = 42); double bar(foo, double); template struct traits { /*...*/ }; } namespace lib { struct base { virtual ~base() {} }; struct foo : base { /*...*/ }; int bar(foo, int = 42); double bar(foo, double); template struct traits { /*...*/ }; } New class layout New argument/return New overload

55 aerix consulting Evolving A Library, Take 1 New library version with interface-breaking changes 55 Copyright 2013 Aerix Consulting namespace lib { // … old interface } namespace lib { // … old interface } What’s wrong with this picture? using namespace v2;

56 aerix consulting Evolving A Library, Take 1 New library version with interface-breaking changes 56 Copyright 2013 Aerix Consulting namespace lib { // … old interface } namespace lib { // … old interface } A new namespace breaks binary compatibility Can’t specialize lib::v2 ’s templates in lib namespace

57 aerix consulting Evolving A Library, Take 1 57 Copyright 2013 Aerix Consulting namespace lib { namespace v2 { template struct traits { /*...*/ }; } using namespace v2; } namespace lib { namespace v2 { template struct traits { /*...*/ }; } using namespace v2; } struct Mine {}; namespace lib { template<> struct traits { /*...*/ }; } struct Mine {}; namespace lib { template<> struct traits { /*...*/ }; } ERROR! Can’t specialize lib::v2 ’s templates in lib namespace

58 aerix consulting Evolving A Library, Take 2 New library version with interface-breaking changes 58 Copyright 2013 Aerix Consulting namespace lib { // … old interface } namespace lib { // … old interface } inline

59 aerix consulting Evolving A Library, Take 1 59 Copyright 2013 Aerix Consulting namespace lib { inline namespace v2 { template struct traits { /*...*/ }; } namespace lib { inline namespace v2 { template struct traits { /*...*/ }; } struct Mine {}; namespace lib { template<> struct traits { /*...*/ }; } struct Mine {}; namespace lib { template<> struct traits { /*...*/ }; } OK! inline

60 aerix consulting Versioning: The Silver (In)Lining Guideline 8: Put all interface elements in a versioning namespace from day one Guideline 9: Make the current version namespace inline 60 Copyright 2013 Aerix Consulting

61 aerix consulting Preventing Name Hijacking Name Hijacking: Unintentional ADL finds the wrong overload 61 Copyright 2013 Aerix Consulting namespace rng { template struct range { Iter begin_, end_; }; template Iter begin( range const & rng ) { return rng.begin_; } template Iter end( range const & rng ) { return rng.end_; } namespace rng { template struct range { Iter begin_, end_; }; template Iter begin( range const & rng ) { return rng.begin_; } template Iter end( range const & rng ) { return rng.end_; } rng::range rng; for( int i : rng ) { std::cout << i << std::endl; } rng::range rng; for( int i : rng ) { std::cout << i << std::endl; }

62 aerix consulting Preventing Name Hijacking Name Hijacking: Unintentional ADL finds the wrong overload 62 Copyright 2013 Aerix Consulting namespace tasks { // Begin anything that looks like // a task. template void begin( TaskLike && t ) { t.Begin(); } struct Task { void Begin() { /*...*/ } }; namespace tasks { // Begin anything that looks like // a task. template void begin( TaskLike && t ) { t.Begin(); } struct Task { void Begin() { /*...*/ } }; rng::range rng; for( tasks::Task t : rng ) { t.Begin(); } rng::range rng; for( tasks::Task t : rng ) { t.Begin(); } $ /usr/local/clang-trunk/bin/clang++ -c -O0 -std=gnu++11 main.cpp -o main.o main.cpp:43:23: error: cannot use type 'void' as an iterator for(tasks::Task t : p2) {} ^ main.cpp:30:10: note: selected 'begin' template [with Task = rng::range &] with iterator type 'void' void begin( Task && t ) ^

63 aerix consulting Preventing Name Hijacking Solution 1: Use a non-inline ADL-blocking namespace 63 Copyright 2013 Aerix Consulting namespace tasks { // Begin anything that looks like // a task. template void begin( TaskLike && t ) { t.Begin(); } namespace block_adl_ { struct Task { void Begin() { /*...*/ } }; } using block_adl_::Task; }; namespace tasks { // Begin anything that looks like // a task. template void begin( TaskLike && t ) { t.Begin(); } namespace block_adl_ { struct Task { void Begin() { /*...*/ } }; } using block_adl_::Task; }; rng::range rng; for( tasks::Task t : rng ) { t.Begin(); } rng::range rng; for( tasks::Task t : rng ) { t.Begin(); } Put type definitions in an ADL- blocking namespace.

64 aerix consulting Preventing Name Hijacking Solution 2: Use global function objects instead of free functions 64 Copyright 2013 Aerix Consulting namespace tasks { // Begin anything that looks like // a task. constexpr struct begin_fn { template void operator()( TaskLike && t ) const { t.Begin(); } } begin {}; struct Task { void Begin() { /*...*/ } }; namespace tasks { // Begin anything that looks like // a task. constexpr struct begin_fn { template void operator()( TaskLike && t ) const { t.Begin(); } } begin {}; struct Task { void Begin() { /*...*/ } }; rng::range rng; for( tasks::Task t : rng ) { t.Begin(); } rng::range rng; for( tasks::Task t : rng ) { t.Begin(); } The begin object cannot ever be found by ADL

65 aerix consulting C++14 Variable Templates! 65 Copyright 2013 Aerix Consulting template struct lexical_cast_fn { template T operator()(U const &u) const { //... } }; template constexpr lexical_cast_fn lexical_cast{}; int main() { lexical_cast ("42"); } template struct lexical_cast_fn { template T operator()(U const &u) const { //... } }; template constexpr lexical_cast_fn lexical_cast{}; int main() { lexical_cast ("42"); } C++14 only

66 aerix consulting Ode To Function Objects They are never found by ADL (yay!) If phase 1 lookup finds an object instead of a function, ADL is disabled (yay!) They are first class objects  Easy to bind  Easy to pass to higher-order functions like std::accumulate 66 Copyright 2013 Aerix Consulting

67 aerix consulting Preventing Name Hijacking Guideline 10: Put type definitions in an ADL-blocking (non-inline!) namespaces and export then with a using declaration, or… 67 Copyright 2013 Aerix Consulting Guideline 11: Prefer global constexpr function objects over named free functions (except for documented customization points)

68 aerix consulting Copyright 2013 Aerix Consulting 68 C++17 We need your contribution Write a proposal!

69 aerix consulting Libraries We Desperately Need File System Databases Networking  Higher-Level Protocols Unicode XML Ranges Graphics! Concurrency 69 Copyright 2013 Aerix Consulting Boost, SG3 SOCI, SG11 SG4 c++netlib  SG9, me! SG13 SG1 IO/Formatting Process Date/time Serialization Trees Compression Parsing Linear Alg Crypto …etc  POCO Boost  POCO, Boost Boost  POCO

70 aerix consulting Getting Involved Get to know your friendly neighborhood C++ Standardization Committee:  http://isocpp.org/std/ http://isocpp.org/std/  http://www.open-std.org/jtc1/sc22/wg21/ http://www.open-std.org/jtc1/sc22/wg21/ Participate in a Study Group:  https://groups.google.com/a/isocpp.org/forum/#!forumsearch/ https://groups.google.com/a/isocpp.org/forum/#!forumsearch/ Get to know Boost.org:  http://www.boost.org http://www.boost.org Take a library, port to C++1[14], propose it! 70 Copyright 2013 Aerix Consulting

71 aerix consulting Thank you Questions? 71 Copyright 2013 Aerix Consulting


Download ppt "Aerix consulting C++11 Library Design Lessons from Boost and the Standard Library."

Similar presentations


Ads by Google