1 / 61

Excel in the Craft and prevent RSI with

Excel in the Craft and prevent RSI with. Stoyan Damov , founder and co-owner, CSGW Ltd. stoyan@csgw.eu. http://www.boost.org/. The evolution of a C++ programmer. Reads a “Teach yourself C++ in 60 seconds” book. Decides he knows C++, adds C++ to his CV.

nitara
Télécharger la présentation

Excel in the Craft and prevent RSI with

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Excel in the Craft and prevent RSI with StoyanDamov, founder and co-owner, CSGW Ltd. stoyan@csgw.eu http://www.boost.org/

  2. The evolution of a C++ programmer Reads a “Teach yourself C++ in 60 seconds” book Decides he knows C++, adds C++ to his CV Reads Bjarne Stroustrup's "The C++ Programming Language" vol. 1 Decides he now knows C++, adds “extensive C++ knowledge” Reads Bjarne Stroustrup's "The C++ Programming Language" vol. 2 Writes “C++ expert” in his CV Reads Scott Meyers's “Effective C++” and “Effective STL” Replaces “expert” with “guru” Reads Herb Sutter's “Exceptional C++” Realizes “guru” is too much, replaces “guru” with “proficient in C++” Reads Andrei Alexandrescu's “Modern C++ Design” Depressed, replaces “proficient” with “experienced” Reads dozens of C++ books, finds Boost, comp.lang.c++(.moderated) Realizes he now knows how to use C++ (and brings back “proficient”)

  3. Experts and gurusare free to go out and enjoy the swimming pool

  4. Excel in the Craft and prevent RSI with "...one of the most highly regarded and expertly designed C++ library projects in the world.“ Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

  5. Петър Димов Boost.Bind Boost.SmartPtr Boost.Member Function Boost.Ref

  6. Top 3 reasons Boost is ignored #1. Pure laziness. “N more libs to learn, why?” I hope you’ll be convinced that learning and using Boost will actually help you work less. #2. “Haven’t checked, but my compiler probably can’t compile it.” You’ll see it’s far from the truth even if you’re stuck with MSVC 6. #3. Sheer ignorance: “Who gives a @#$%?” Apparently doesn’t apply to all of you who attend this lecture :)

  7. Some reasons to use Boost You’ll type less code. I’ll show you. Makes C++ a more compelling language by overcoming language and library limitations. Drives the language (10 libs in TR1, more will be in TR2) and compilers. Extends and works well with the Standard Library. Portable across enough compilers and OS-es. Free and non-proprietary. Used by top software companies like Adobe, SAP, Real Networks, McAffee, (ahem) CSGW, etc.

  8. This lecture will introduce you to Boost and teach you how to: Simplify object lifetime management and resource sharing with Boost.SmartPtr Define function objects at call site and bind arguments to N-arity free functions, member functions and functors with Boost.Bind and Boost.Lambda Define, store and implement callbacks with Boost.Function Trigger and handle events (multicast callbacks) with Boost.Signals

  9. This lecture will introduce you to Boost and teach you how to: Store and retrieve values of any type with Boost.Any and Boost.Variant Process text efficiently with Boost.Tokenizer, Boost.StringAlgo, Boost.Regex, Boost.Xpressive and Boost.Spirit Write portable multithreaded code with Boost.Thread Marshal and unmarshal objects in a versioned and platform-independant manner with Boost.Serialization Use specialized containers when STL's are not (efficient) enough from Boost.MultiArray, Boost.MultiIndex and Boost.Intrusive

  10. 1 Raw Pointers 101 void leak_memory_recipe() { T* p(new T()); if (some_condition) { return; } delete p; } void shoot_your_self_in_the_foot() { T* p(new T()); free(p); // might launch Tetris delete p; delete p; // might format HDD U* q(new U[42]); delete q; // might work in MSVC :) void* p = malloc(sizeof(A)); A* a = new(p) A(); delete a; // might launch PacMan }

  11. 2 Raw Pointers void contrived_example() { // Windows Mobile -- thread stack is 8K: // allocate on heap to prevent stack overflow T* p(new T()); if (!foo(p->something())) { delete p; return; // slides suck } U* q(new U()); if (!bar(“bar”, q)) { delete p; delete q; return; } p->do_something_useful_with(q); delete p; q->do_something_less_useful(); delete q; } • Ugly • Error-prone Can leak p & q in 3 different ways tomorrow. How? // today bool bar(char const* p, U* u); // tomorrow bool bar(Bar const& bar, U* u) throw (...); struct Bar { Bar(std::string const& s) throw (...); };

  12. 3 Dumb Pointers template <typename T> struct dumb_ptr { explicit dumb_ptr(T* p) : p_(p) {} ~dumb_ptr() { delete p_; } private: // non-copyable “stuff” omitted T* p_; }; void hand_crafted_dumb_pointers() { T* p(new T()); dumb_ptr<T> g1(p); if (!foo(p->something())) { return; } U* q(new U()); dumb_ptr<U> g2(q); if (!bar(“bar”, q)) { return; } p->do_something_useful_with(q); q->do_something_less_useful(); } // at end-of-scope, g1 and g2 delete pointees

  13. 4 Smart Pointers void enter_auto_ptr() { auto_ptr<T> p(new T()); if (!foo(p->something())) { return; } auto_ptr<U> q(new U()); if (!bar(“bar”, q)) { return; } p->do_something_useful(); q->do_something_less_useful(); } // at end-of-scope, p and q delete pointees // auto_ptr defined in <memory>

  14. 5 Smart Pointers struct S { // ... private: auto_ptr<T> p_; }; // Q: who owns T*? (hint: elipsis are misleading) • auto_ptr<T> • sole ownership • transferable ownership • intention hard to guess struct S { auto_ptr<T> good_bye() { return p_; } T* farewell() { return p_.release(); } private: auto_ptr<T> p_; }; // A: whoever calls good_bye or farewell first

  15. 6 Smart Pointers void launch_tetris() { auto_ptr<char> buffer(new char[42]); }; • auto_ptr<T> • sole ownership • transferable ownership • intention hard to guess • can’t be used with arrays

  16. 7 Smarter Pointers • scoped_ptr<T> • sole ownership • non-transferable ownership • intention at first glance struct S { private: boost::scoped_ptr<T> p_; }; // It’s all in the name: S owns T* template<class T> class scoped_ptr : noncopyable { ... // no release() function }

  17. 8 Smarter Pointers void launches_tetris_as_well() { scoped_ptr<char> buffer(new char[42]); }; • scoped_ptr<T> • sole ownership • non-transferable ownership • intention at first glance • cannot be used with arrays but: template<class T> classscoped_array : noncopyable { ... } void foo() { scoped_array<char> buffer(new char[42]); }

  18. 9 Raw Pointers (again) // every non-trivial multithreaded application // passes data between threads void who_deletes_the_pointer() { T* p(new T()); launch_thread_and_pass_it(p); p->do_something_useful(); } Кой, я ли не моа?

  19. 10 Reference-counted pointers struct T : private noncopyable { T() : refs_(1) {} void add_ref() { ++refs_; } void release() { if (--refs_ == 0) { deletethis; } } void do_something_useful() { ... } private: size_t refs_; }; // continued on next slide…

  20. 11 Reference-counted pointers void who_deletes_the_pointer() { T* p(new T()); // refs_ == 1 launch_thread_and_pass_it(p); // refs_ == 2 p->do_something_useful(); /*a*/ p->release(); // refs_ == 1 or 0? } Pray these don’t throw OR abuse try/catch constructs void launch_thread_and_pass_it(T* p) { p->add_ref(); // refs_ == 2 // actually launch thread and pass it p } void thread(T* p) { // refs_ == 2 or 1 p->do_something_useless(); /*b*/ p->release(); // refs_ == 1 or 0? } Ugly, scattered, error-prone reference counting

  21. 12 Reference-counted pointers launch_thread_and_pass_it(p); // refs_ == 2 voidwho_deletes_the_pointer() // thread 1 (T1) /*a*/ p->release(); void thread(T* p) // thread 2 (T2) /*b*/ p->release(); Multithreading 101: Race conditions 1. refs_ = 2; (memory) 2. Main thread (T1) reads the value of refs_ from memory into register 1 (r1) : 2 3. Secondary thread (T2) reads the value of refs_ from memory into register 2 (r2) : 2 4. T1 decrements the value of refs_ in r1: (r1 contents) - 1 = 1 5. T2 decrements the value of refs_ in r2: (r2 contents) - 1 = 1 6. T1 stores the value of r1 in memory : 1 7. T2 stores the value of r2 in memory : 1 8. refs_ = 1; (memory) Pointer leaked.

  22. 13 Almost there: thread-safe Reference-counted pointers struct T : privatenoncopyable { T() : refs_(1) {} voidadd_ref() { impl_atomic_increment(refs_); } void release() { if (impl_atomic_decrement(refs_) == 0) { deletethis; } } private: implementation-specific refs_; };

  23. 14 boost::shared_ptr • shared_ptr<T> • shared ownership • thread-safe • cannot be used with arrays but • shared_array<T> can. typedef boost::shared_ptr<T> Ptr; void last_shared_ptr_deletes_the_pointer() { Ptr p(new T()); launch_thread_and_pass_it(p); p->do_something_useful(); } void launch_thread_and_pass_it(Ptr p) { // launch thread and pass it p } void thread(Ptr p) { p->do_something_useless(); }

  24. 15 A few well-known concepts CopyConstructible struct T { T(T const& other); }; Assignable struct T { T& operator=(T const& other); }; NonCopyable struct S { private: T(T const& other); // not CopyConstructible T& operator=(T const& other); // not Assignable };

  25. 16 Why do I want to know these? Standard Library Containers require the template parameter used for the containers’ element type to be bothCopyConstructible and Assignable. auto_ptr<T>, scoped_ptr<T>, and scoped_array<T> do not meet these requirements and so cannot be used in STL containers.

  26. 17 STL containers with raw pointers struct Big { ... }; struct T { // ... ~T(); private: // Big is expensive to copy, use pointers typedef std::vector<Big*> BigItems; typedef BigItems::iterator iterator; BigItems items_; }; T::~T // don’t forget to delete the pointers! { iterator e(items_.end()); for (iterator b(items_.begin()); b != e; ++b) { delete *b; } }

  27. 18 STL containers with raw pointers template <typename C> void clear_container(C& c) { std::for_each(c.begin(), c.end(), Deleter()); } struct Deleter { template <typename T> void operator()(T const* p) const { delete p; } }; T::~T { clear_container(items_); } operator() is member template so Deleter can deduce its argument

  28. 19 STL containers with shared_ptr struct Big { ... }; struct T { private: typedef boost::shared_ptr<Big> BigPtr; typedef std::vector<BigPtr> BigItems; BigItems items_; }; No, really, that’s it :)

  29. 20 More STL containers with raw pointers struct Big { ... /* defines operator< */ }; struct T { private: struct BigPtrLess { booloperator()( Big const* l, Big const* r) const { return *l < *r; } }; typedef std::set<Big*, BigPtrLess> BigItems; BigItems items_; };

  30. 21 More STL containers with shared_ptr (déjà vu) struct Big { ... }; struct T { private: typedef boost::shared_ptr<Big> BigPtr; typedef std::vector<BigPtr> BigItems; BigItems items_; }; struct Big { ... /* defines operator< */ }; struct T { private: typedef boost::shared_ptr<Big> BigPtr; typedef std::set<BigPtr> BigItems; BigItems items_; }; shared_ptr defines relational operators so pointers work as desired

  31. 22 shared_ptr in a nutshell • shared ownership - underlying pointer deleted when the last • shared_ptr pointing to it is destroyed or reset • copy-constructible and assignable, so can be used in • STL containers • comparison operators are defined, so can be used in • associative containers • part of TR1 • eliminates explicit deletes in code • decreases the need for try/catch constructs • thread-safe • What’s not to like?!

  32. 23 shared_ptr and cyclic references struct Source; typedef shared_ptr<Source> SourcePtr; struct Sink { virtual void event_occurred() = 0; }; typedef shared_ptr<Sink> SinkPtr; struct Source { virtual void set_observer(SinkPtr observer) = 0; }; struct SourceImpl : Source, private noncopyable { void set_observer(SinkPtr observer); private: SinkPtr observer_; };

  33. 24 shared_ptr and cyclic references struct SinkImpl : Sink, private noncopyable { SinkImpl(SourcePtr source) : source_(source) {} void event_occurred() { ... } private: SourcePtr source_; }; void SourceImpl::set_observer(SinkPtr observer) { observer_ = observer; // Game Over: cyclic reference, both pointers leaked } void SourceImpl::fire_event() { if (observer_) observer_->event_occurred(); } SourcePtr source(new SourceImpl()); SinkPtr sink(new SinkImpl(source)); source->set_observer(sink); source->fire_event(); SinkImpl now has a strong reference to Source SourceImpl now has a strong reference to Sink

  34. 25 Enter boost::weak_ptr struct Source; typedef shared_ptr<Source> SourcePtr; struct Sink { virtual void event_occurred() = 0; }; typedef shared_ptr<Sink> SinkPtr; struct SourceImpl : Source { void set_observer(SinkPtr observer); private: weak_ptr<Sink> observer_; }; We still take a shared_ptr parameter but store a weak_ptr

  35. 26 Enter weak_ptr (continued) struct SinkImpl { SinkImpl(SourcePtr source) : source_(source) {} void event_occurred() { ... } private: SourcePtr source_; }; void SourceImpl::set_observer(SinkPtr observer) { observer_ = observer; } void SourceImpl::fire_event() { if (SinkPtr p = observer_.lock()) { p->event_occurred(); } } SourcePtr source(new SourceImpl()); ... shared_ptr can be used in boolean expressions, handy in conditional assignments like this lock() returns shared_ptr<Sink>

  36. 27 boost::enable_shared_from_this struct SinkImpl : Sink, enable_shared_from_this<Sink> { // SinkImpl(SourcePtr source) : source_(source) {} void set_source(SourcePtr source) { source_ = source; source_->set_observer(shared_from_this()); } // ... }; #include <boost/enable_shared_from_this.hpp>

  37. 28 But wait, there’s more!!! string you_can_do_this_as_well() { char* raw = reinterpret_cast<char*>(malloc(1024)); shared_array<char> mem(raw, free); call_an_api_function(mem.get()); return raw; } // free(mem.get()) called at end of scope you can pass a custom deleter // RAII is about resources, not just memory void process_file(char const* file_path) { shared_ptr<FILE> file(fopen(file_path, “r”), fclose); read_data_from_file(file.get()); } // fclose(file.get()) called at end of scope

  38. If you’re still not convinced you missed Boost’s smart pointers you’re a C, not C++ programmer

  39. 29 template <typenameInputIterator, typename Predicate> InputIteratorfind_if( InputIterator first, InputIterator last, Predicate pred); // Predicate: function object that defines // the condition to be satisfied by the element // being searched for. // A predicate takes single argument and returns // true or false. Standard Library Binders bind1st A helper template function that creates an adaptor to convert a binary function object into a unary function object by binding the first argument of the binary function to a specified value. #include <boost/assign/std/vector.hpp> // operator+=() using namespace boost::assign; typedef std::vector<int> Numbers; typedef Numbers::iterator iterator; Numbers nums; nums += 6, 9, 42, 54, 13; // yup, boost::assign rocks iterator answer = find_if( nums.begin(), nums.end(), bind1st(equal_to<int>(), 42)); assert(answer != nums.end() && *answer == 42); template <typename Operation, typename Type> binder1st<Operation> bind1st( Operation const& func, Type const& left); template <typename Type> structequal_to : binary_function<Type, Type, bool> { bool operator()( Type const& left, Type const& right) const; };

  40. 30 Standard Library Binders bind2nd A helper template function that creates an adaptor to convert a binary function object into a unary function object by binding the second argument of the binary function to a specified value. // ... nums += 6, 9, 42, 54, 13; // yup, boost rocks iterator it = find_if( nums.begin(), nums.end(), bind2nd(greater<int>(), 13)); assert(it != nums.end() && *it == 42); template <typename Operation, typename Type> binder1st<Operation> bind2nd( Operation const& func, Type const& right);

  41. 31 Boost.Bind boost::bind Generalization of the standard functions std::bind1st and std::bind2nd. Supports arbitrary function objects, functions, function pointers, and member function pointers, and is able to bind any argument to a specific value or route input arguments into arbitrary positions. Warning, magic ahead #include <boost/bind.hpp> nums += 6, 9, 42, 54, 13; iterator fourty_two = find_if( nums.begin(), nums.end(), bind(equal_to<int>(), 42, _1)); iterator it = find_if( nums.begin(), nums.end(), bind(greater<int>(), _1, 13)); // note: _1, not _2 a “this ain’t C++” WTF moment: _1 is a placeholder emulating bind1st emulating bind2nd

  42. 32 Boost.Bind Works with functions and function pointers int add(int x, int y) { return x + y; } int div(int x, int y) { return x / y; } bind(add, _1, 7) // becomes unary function add(x, 7) // equivalent to bind2nd(ptr_fun(add), 7) // bind1st emulated as well bind(add, 7, _1) // becomes unary function add(7, y) bind(add, _1, 7)(3) // == add(3, 7) == 10 bind(add, 5, 7) // becomes nullary function add(5, 7) bind(add, 5, 7)() // == 12 bind(add, 5, 7)(13, 42) // == 12 (extra args ignored) // function pointers int (*pf)(int, int) = add; bind(pf, _2, _1)(2, 84) // pf(84, 2) == 42 btw, placeholders need not be in order

  43. 33 Boost.Bind bind3rd, bind4th..., bind<N>th int f(int x, int y, int z); int g(int a, int b, int c, int d); bind(f, _1, _2, _3)(5, 6, 7) // f(5, 6, 7) bind(f, _1, 42, _2)(5, 13) // f(5, 42, 13) bind(f, _2, _1, 42)(5, 13) // f(13, 5, 42) bind(f, _1, _1, _1)(42, 43) // f(42, 42, 42) bind(g, 1, _1, 3, 4)(2) // g(1, 2, 3, 4)

  44. 34 Boost.Bind boost::ref in <boost/ref.hpp> avoids (expensive) copying of function objects Works with function objects (functors) struct Greeter { void operator()(string const& name) const { cout << “Hi “ << name << endl; } }; Greeter g; bind<void>(g, _1)(“Boost”); // outputs “Hi Boost” bind<void>(ref(g), _1)(“Boost”); // ditto bind<void>(Greeter(), _1)(“Boost”); // ditto operator()’s return type must be explicitly specified When the functor exposes a nested type named result_type (e.g. STL’s functors), the explicit return type can be omitted (but some compilers simply suck so it won’t work everywhere) bind(less<int>(), _1, 42)

  45. 35 Boost.Bind Works with pointers to members bind(&T::f, args) struct T { void f(int val); }; T t; shared_ptr<T> p(new T); int v = 42; bind(&T::f, &t, _1)(v) // (&t)->f(v) bind(&T::f, ref(t), _1)(v) // t.f(v) bind(&T::f, t, _1)(v) // (internal-copy-of-t).f(v) bind(&T::f, p, _1)(v) // (internal-copy-of-p)->f(v) // the last two produce self-contained function objects // (a.k.a. closures)

  46. 36 Boost.Bind Overloads operator! and relational operators struct P // Participant – abbreviated to save space { explicit P(string const& name) : name_(name) {} P(P const& o) : name_(o.name_) {} P& operator=(P const& o) { return name_ = o.name_, *this; // one-liner to save space } string name_; }; typedef vector<P> Participants; typedef Participants::iteratoriterator; Participants guys; guys += P("Stanimirov"), P("Nakov"), P("Nakov"), P("Penchev"); // ... iteratori = adjacent_find(guys.begin(), guys.end(), !(bind(&P::name_, _1) != bind(&P::name_, _2))); i = find_if(guys.begin(), guys.end(), bind(&P::name_, _1) == “Penchev”); sort(guys.begin(), guys.end(),// Stanimirov, Penchev, Nakov, Nakov bind(&P::name_, _2) < bind(&P::name_, _1)) !(x != y) to demonstrate overloaded operator ! - equivalent to bind(...name_, _1) == bind(...name_, _2)

  47. 37 Boost.Lambda # Ruby Language = Struct.new(:name, :desc) # names = %w{ Ruby C# C++ } # languages = names.collect { | n | Language.new(n, 'cool') } languages.collect { | lang | print lang.name, ' ' } # outputs: Ruby C# C++ // C# 2 langs.ForEach(delegate(Language lang) { Console.Write("{0} ", lang.Name); }); // C# 3 langs.ForEach(lang => Console.Write("{0} ", lang.Name)); // C++ before Boost.Lambda transform(langs.begin(), langs.end(), ostream_iterator<string>(cout, " "), bind(&L::name_, _1)); // C++ after Boost.Lambda for_each(langs.begin(), lang.end(), cout << bind(&L::name_, _1) << ' '); #include <boost/bind.hpp> using namespace boost; Warning, magic ahead #include <boost/lambda/lambda.hpp> #include <boost/lambda/bind.hpp> using namespace boost; using namespace boost::lambda;

  48. 38 Boost.Lambda string name; for_each( langs.begin(), lang.end(), ( var(name) = bind(&L::name_, _1), cout << var(name) << " is ", if_(var(name) == "C++") [cout << constant(“smart")] .else_ [cout << constant("dumb")], cout << constant(“\n")) )); // Outputs: // Ruby is dumb // C# is dumb // C++ is smart brackets are necessary, for_each accepts 3 arguments commas chain lambda expressions Yes, this is C++ delays evaluation of constants and variables Lambda expressions for control structures (flow control) Boost.Lambda supports: if-then[-else], while, do-while, for, switch; also try-catch, construction, cast, etc. expressions

  49. 39 Boost.Function Stores function pointers and function objects for subsequent invocation. Anywhere where a function pointer would be used to defer a call or make a callback, Boost.Function can be used instead to allow the user greater flexibility in the implementation of the target. Targets can be any 'compatible' function object (or function pointer), meaning that the arguments to the interface designated by Boost.Function can be converted to the arguments of the target function object Preferred syntaxPortable syntax function<void()> f; function0<void> f; function<bool(int n)> f; function1<bool,int> f; digit after “function” defines function arity (number of args)

  50. 40 Boost.Function function<bool(int)> f; Free Functions boolfoo(int bar); f = foo; // f = &foo for MSVC 6 devs bool ret = f(42); // same as foo(42); Functors structT { booloperator()(int bar); }; f = T(); bool ret = f(42); // same as T::operator()(42); // if T is expensive to copy T t; f = ref(t); // boost::ref

More Related