excel in the craft and prevent rsi with stoyan damov, founder and co-owner, csgw ltd.csgw ltd....

61
l in the Craft and prevent RSI Stoyan Damov, founder and co- owner, CSGW Ltd. [email protected] ttp://www.boost.org/

Upload: albert-green

Post on 26-Dec-2015

222 views

Category:

Documents


2 download

TRANSCRIPT

Excel in the Craft and prevent RSI with

Stoyan Damov, founder and co-owner, CSGW Ltd.

[email protected]://www.boost.org/

The evolution of a C++ programmerReads 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”)

Experts and gurus are free togo out and enjoy the swimming pool

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

Boost.Bind

Boost.Member Function

Boost.Ref

Boost.SmartPtr

Петър Димов

#1. Pure laziness. “N more libs to learn, why?”I hope you’ll be convinced that learning and usingBoost will actually help you work less.

#2. “Haven’t checked, but my compiler probablycan’t compile it.”You’ll see it’s far from the truth even if you’re stuckwith MSVC 6.

#3. Sheer ignorance: “Who gives a @#$%?”Apparently doesn’t apply to all of you who attendthis lecture :)

Top 3 reasons Boost is ignored

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.

Some reasons to use Boost

This lecture will introduce you toBoost 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

This lecture will introduce you toBoost 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

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 Tetrisdelete 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

}

Raw Pointers 1011

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;

}

Raw Pointers

// today

bool bar(char const* p, U* u);

• Ugly• Error-prone

Can leak p & q in 3 different ways tomorrow. How?

// tomorrow

bool bar(Bar const& bar, U* u) throw (...);

struct Bar{ Bar(std::string const& s) throw (...);};

2

Dumb Pointerstemplate <typename T>struct dumb_ptr{

explicit dumb_ptr(T* p) : p_(p) {}~dumb_ptr() { delete p_; }

private: // non-copyable “stuff” omittedT* 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

3

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>

4

Smart Pointersstruct S{

// ...private:

auto_ptr<T> p_;};

// Q: who owns T*? (hint: elipsis are misleading)

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

auto_ptr<T>

• sole ownership• transferable ownership• intention hard to guess

auto_ptr<T>

• sole ownership• transferable ownership• intention hard to guess

5

Smart Pointersvoid 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

auto_ptr<T>

• sole ownership• transferable ownership• intention hard to guess• can’t be used with arrays

6

Smarter Pointersstruct S{private:

boost::scoped_ptr<T> p_;};

// It’s all in the name: S owns T*

scoped_ptr<T>

• sole ownership• non-transferable ownership• intention at first glance

scoped_ptr<T>

• sole ownership• non-transferable ownership• intention at first glance

template <class T> class scoped_ptr : noncopyable{

...// no release() function

}

7

void launches_tetris_as_well(){

scoped_ptr<char> buffer(new char[42]);};

Smarter Pointers

template <class T> class scoped_array : noncopyable{

...}

void foo(){

scoped_array<char> buffer(new char[42]);}

scoped_ptr<T>

• sole ownership• non-transferable ownership• intention at first glance• cannot be used with arrays but:

scoped_ptr<T>

• sole ownership• non-transferable ownership• intention at first glance• cannot be used with arrays but:

8

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();

}

Кой, я ли не моа?

9

Reference-counted pointersstruct T : private noncopyable{

T() : refs_(1) {}void add_ref() { ++refs_; }void release(){

if (--refs_ == 0){

delete this;}

}void do_something_useful() { ... }

private:size_t refs_;

};

// continued on next slide…

10

void who_deletes_the_pointer(){

T* p(new T()); // refs_ == 1launch_thread_and_pass_it(p); // refs_ == 2p->do_something_useful();

/*a*/ p->release(); // refs_ == 1 or 0?}

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 1p->do_something_useless();

/*b*/ p->release(); // refs_ == 1 or 0?}

Pray these don’t throwOR

abuse try/catch constructs

Ugly, scattered, error-prone reference

counting

Reference-counted pointers11

launch_thread_and_pass_it(p); // refs_ == 2

void who_deletes_the_pointer() // thread 1 (T1)/*a*/ p->release();

void thread(T* p) // thread 2 (T2)/*b*/ p->release();

Multithreading 101: Race conditions1. refs_ = 2; (memory)2. Main thread (T1) reads the value of refs_ from memory into register 1 (r1) : 23. Secondary thread (T2) reads the value of refs_ from memory into register 2 (r2) : 24. T1 decrements the value of refs_ in r1: (r1 contents) - 1 = 15. T2 decrements the value of refs_ in r2: (r2 contents) - 1 = 16. T1 stores the value of r1 in memory : 17. T2 stores the value of r2 in memory : 18. refs_ = 1; (memory)

Pointer leaked.

Reference-counted pointers12

struct T : private noncopyable{

T() : refs_(1) {}void add_ref(){

impl_atomic_increment(refs_);}void release(){

if (impl_atomic_decrement(refs_) == 0){

delete this;}

}private:

implementation-specific refs_;};

Almost there: thread-safeReference-counted pointers

13

boost::shared_ptrtypedef 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();}

shared_ptr<T>

• shared ownership• thread-safe• cannot be used with arrays butshared_array<T> can.

shared_ptr<T>

• shared ownership• thread-safe• cannot be used with arrays butshared_array<T> can.

14

A few well-known conceptsCopyConstructible

struct T{ T(T const& other);};

Assignablestruct T{ T& operator=(T const& other);};

NonCopyablestruct S{private: T(T const& other); // not CopyConstructible T& operator=(T const& other); // not Assignable};

15

Why do I want to know these?

Standard Library Containers require thetemplate parameter used for the containers’element type to be both CopyConstructible

and Assignable.

auto_ptr<T>, scoped_ptr<T>, andscoped_array<T> do not meet theserequirements and so cannot be used inSTL containers.

16

STL containers with raw pointersstruct Big { ... };

struct T{

// ...~T();

private:// Big is expensive to copy, use pointerstypedef 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;}

}

17

STL containers with raw pointerstemplate <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

18

STL containers with shared_ptrstruct Big { ... };

struct T{private:

typedef boost::shared_ptr<Big> BigPtr;typedef std::vector<BigPtr> BigItems;BigItems items_;

};

No, really, that’s it :)

19

More STL containers withraw pointers

struct Big { ... /* defines operator< */ };

struct T{private:

struct BigPtrLess{

bool operator()(Big const* l,Big const* r) const

{return *l < *r;

}};typedef std::set<Big*, BigPtrLess> BigItems;BigItems items_;

};

20

More STL containers withshared_ptr (déjà vu)

struct Big { ... /* defines operator< */ };

struct T{private:

typedef boost::shared_ptr<Big> BigPtr;typedef std::set<BigPtr> BigItems;BigItems items_;

};

struct Big { ... };

struct T{private:

typedef boost::shared_ptr<Big> BigPtr;typedef std::vector<BigPtr> BigItems;BigItems items_;

};

shared_ptr defines relational operators so

pointers work as desired

21

shared_ptr in a nutshell

• shared ownership - underlying pointer deleted when the lastshared_ptr pointing to it is destroyed or reset

• copy-constructible and assignable, so can be used inSTL containers

• comparison operators are defined, so can be used inassociative containers

• part of TR1• eliminates explicit deletes in code• decreases the need for try/catch constructs• thread-safe

What’s not to like?!

22

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_;};

shared_ptr and cyclic references23

shared_ptr and cyclic referencesstruct 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

24

Enter boost::weak_ptrstruct 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

25

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()); ...

Enter weak_ptr (continued)

lock() returns shared_ptr<Sink>

shared_ptr can be used inboolean expressions, handy in

conditional assignments like this

26

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()); } // ...};

boost::enable_shared_from_this

#include <boost/enable_shared_from_this.hpp>

27

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

But wait, there’s more!!!

// 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

you can pass a custom deleter

28

If you’re still not convincedyou missed Boost’s smart

pointers you’rea C, not C++ programmer

Standard Library Bindersbind1stA helper template function that creates an adaptor to convert abinary function object into a unary function object by binding thefirst 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>struct equal_to : binary_function<Type, Type, bool> { bool operator()( Type const& left, Type const& right) const;};

template <typename Type>struct equal_to : binary_function<Type, Type, bool> { bool operator()( Type const& left, Type const& right) const;};

template <typename InputIterator, typename Predicate>InputIterator find_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.

29

Standard Library Bindersbind2ndA helper template function that creates an adaptor to convert abinary function object into a unary function object by binding thesecond 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);

30

Boost.Bindboost::bindGeneralization of the standard functions std::bind1st and std::bind2nd.Supports arbitrary function objects, functions, function pointers, andmember function pointers, and is able to bind any argument to a specificvalue or route input arguments into arbitrary positions.

#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

Warning,magic ahead

31

Boost.BindWorks 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 tobind2nd(ptr_fun(add), 7)// bind1st emulated as wellbind(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)() // == 12bind(add, 5, 7)(13, 42) // == 12 (extra args ignored)

// function pointersint (*pf)(int, int) = add;bind(pf, _2, _1)(2, 84) // pf(84, 2) == 42

btw, placeholders need not be in order

32

Boost.Bindbind3rd, 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)

33

Boost.BindWorks 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”); // dittobind<void>(Greeter(), _1)(“Boost”); // ditto

bind(less<int>(), _1, 42)

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)

operator()’s return type must be explicitly specified

boost::ref in <boost/ref.hpp>avoids (expensive) copying of function objects

34

Boost.BindWorks 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)

35

Boost.BindOverloads operator! and relational operatorsstruct 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::iterator iterator;

Participants guys;guys += P("Stanimirov"), P("Nakov"), P("Nakov"), P("Penchev"); // ...

iterator i = 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, Nakovbind(&P::name_, _2) < bind(&P::name_, _1))

!(x != y) to demonstrate overloaded operator ! - equivalent to

bind(...name_, _1) == bind(...name_, _2)

36

Boost.Lambda# RubyLanguage = 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# 2langs.ForEach(delegate(Language lang)

{ Console.Write("{0} ", lang.Name); });

// C# 3langs.ForEach(lang => Console.Write("{0} ", lang.Name));

// C++ before Boost.Lambdatransform(langs.begin(), langs.end(), ostream_iterator<string>(cout, " "), bind(&L::name_, _1));

// C++ after Boost.Lambdafor_each(langs.begin(), lang.end(),

cout << bind(&L::name_, _1) << ' ');

#include <boost/bind.hpp>using namespace boost;#include <boost/bind.hpp>using namespace boost;

#include <boost/lambda/lambda.hpp>#include <boost/lambda/bind.hpp>using namespace boost;using namespace boost::lambda;

#include <boost/lambda/lambda.hpp>#include <boost/lambda/bind.hpp>using namespace boost;using namespace boost::lambda;

37

Warning,magic ahead

Boost.Lambdastring 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

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

delays evaluation of constants and variables

commas chain lambda expressions

Yes, thisis C++

38

Boost.FunctionStores 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 syntax Portable syntax

function<void()> f; function0<void> f;function<bool(int n)> f; function1<bool, int> f;

digit after “function” definesfunction arity (number of args)

39

Boost.Functionfunction<bool(int)> f;

Free Functionsbool foo(int bar);f = foo; // f = &foo for MSVC 6 devsbool ret = f(42); // same as foo(42);

Functorsstruct T{

bool operator()(int bar);};f = T();bool ret = f(42); // same as T::operator()(42);

// if T is expensive to copyT t;f = ref(t); // boost::ref

40

Boost.Functionfunction<bool(T*, int)> f;

Member functionsstruct T{

bool foo(int bar);};

f = &T::foo;

T t;

f(&t, 42);

41

Boost.FunctionUsage

template <typename T>struct ThreadSafeContainer{

vector<T> items_;mutex mx_; // perhaps boost::mutex

};

// how to iterate its items in a thread-safe manner?

struct ThreadSafeContainer{

// 1) return a copyvector<T> return_copy(){

mutex::scoped_lock lock(mx_);return vector<T>(items_.begin(), items_.end());

}// expensive :(

};

42

Boost.FunctionUsage

// how to iterate its items in a thread-safe manner?

struct ThreadSafeContainer{

// 2) expose the mutexmutex& get_mx(){

return mx_;}// error-prone, exposes implementation details

};

43

Boost.FunctionUsage

// how to iterate its items in a thread-safe manner?

struct ThreadSafeContainer{ // 3) enumeration while locked void enumerate(function<void(T const& value)> enumerator) { mutex::scoped_lock lock(mx_); for_each(items_.begin(), items_.end(), enumerator); }};

ThreadSafeContainer<int> cont;// ...fill it in

// iterate in a thread-safe mannercont.enumerate(cout << _1 << constant("\n"));

44

Boost.Signals

Managed signals and slots system

Signal: callback with multiple targets (multicast callback)a.k.a. publishera.k.a. event

Signals are connected to a set of slots (callback receivers)a.k.a. subscribersa.k.a. event targets

Makes Java’s listeners and .NET’s events laughable.

45

Boost.SignalsPreferred syntax Portable syntax

signal<void()> event; signal0<void> event;signal<bool(int n)> sig; signal1<bool, int> sig;

event(); // fire event - no slots connected, nothing happens

// single slotvoid foo() { cout << “foo” << endl; }event.connect(&foo);event(); // outputs “foo”, new line

// multiple slots (foo and bar) connectedvoid bar() { cout << “bar” << endl; }event.connect(&foo);event.connect(&bar);event(); // outputs “foo”, new line, “bar”, new line

digit after “signal”declares arity

(number of args)

can have return value

46

Boost.SignalsOrdering slotssignal<void()> sig;

void foo() { cout << “foo”; }void bar() { cout << “bar”; }void eol() { cout << endl; }

sig.connect(&eol);sig.connect(1, &bar);sig.connect(0, &foo);sig(); // outputs “foobar”, end-of-line

Passing values to slotssignal<void(int, int)> sig;

void sum(int x, int y) { cout << x << "+" << y << "=" << x + y; }void mul(int x, int y) { cout << x << "*" << y << "=" << x * y; }

sig.connect(&sum);sig.connect(&mul);sig(6, 7); // outputs: “6 + 7 = 13” and “6 * 7” = 42

slot order is defined by an optional 1st

parameter

non-grouped slots called last

47

Boost.SignalsObtaining values from slotssignal<int(int, int)> calc;

int sum(int x, int y) { return x + y; }int mul(int x, int y) { return x * y; }

sig.connect(&sum);sig.connect(&mul);

int meaningless_result = sig(6, 7);

// ^^^ returns last called slot’s result

48

Boost.SignalsObtaining values from slotstemplate <typename T> struct maximum{ typedef T result_type;

template <typename Iter> T operator()(Iter first, Iter last) const { // No slots to call -> return default-constructed value if (first == last) { return T(); }

T max_value = *first++; while (first != last) { if (max_value < *first) { max_value = *first; } ++first; } return max_value; }};

signal<int(int, int), maximum<int> > calc;int max = sig(6, 7); // max(13, 42) == 42

maximum is a combiner

combiner is a mechanism that can take the results of calling slots and coalesces them into a single result to be returned to the caller

allows for type deduction of signal’s return value

The input iterators passed to the combiner transform dereference operations into slot calls. This pull-based interface makes writing combiners a lot more easier.

49

Boost.SignalsObtaining values from slotstemplate <typename C> struct aggregator{ typedef C result_type;

template <typename Iter> T operator()(Iter first, Iter last) const { return C(first, last); }};

signal<int(int, int), aggregator<vector<int> > > calc;vector<int> all_results(sig(6, 7));

50

Boost.ICE