440 likes | 509 Views
Higher order programming. Using C++ and boost. C++ concepts. // T must model MyFuncConcept template<class T> generic_function(T t) { t.MyFunc(); }. C++ concepts. T is a model of MyFuncConcept if T has a member function called MyFunc which takes no arguments. C++ concepts.
E N D
Higher order programming Using C++ and boost
C++ concepts // T must model MyFuncConcept template<class T> generic_function(T t) { t.MyFunc(); }
C++ concepts T is a model of MyFuncConcept if T has a member function called MyFunc which takes no arguments
C++ concepts struct MyFuncClass { void MyFunc() {...} }; { MyFuncClass mfc; generic_function(mfc); // Compiles int i; generic_function(i); // Compilation error }
C++ concepts • An assumption about a type parameter • Compilation error if assumption fails • Language support for concepts will be added in C++0X template <DefaultConstructible T> ...
Polymorphism through concepts Compile-time polymorphism through ”impicit interfaces” template<class T> void foo(T& t) { ... t.bar(); ... } Run-time polymorphism through explicit interfaces class FooType { virtual void bar() = 0; }; void foo(FooType& t) { ... t.bar(); ... }
Sidenote: boost::concept_check • Provide easy to read compilation errors • Earlier compilation errors • Helps writing understandable code
boost::concept_check example template <class RandomAccessIter> void stable_sort(RandomAccessIter first, RandomAccessIter last) { using namespace boost; function_requires< RandomAccessIteratorConcept<RandomAccessIter> >(); typedef typename std::iterator_traits<RandomAccessIter>::value_type value_type; function_requires< LessThanComparableConcept<value_type> >(); ... }
Higher order programming Passing functions as values Having values requires a type
C++ function pointers • Unintuitive syntax • Casting between member function pointers problematic • Pointers to virtual functions problematic • Different compilers behave differentally
C++ concept of a functor A class which defines operator() struct SomeFunctor { void operator()() {...} }; template <class F> void foo(F f) { ... f(); ... }
C++ concept of a functor Some STL algorithms also require internal typedefs for return_type, first_argument_type, second_argument_type struct SomeFunctor { typedef bool return_type; typedef int first_argument_type; bool operator()(int x) {...} };
Hand made functor examples Can take various amounts of arguments struct MyNullaryFunctor { void operator()(); }; struct MyUnaryFunctor { void operator()(int arg1); };
Hand made functor examples Can hold state struct NullaryFunctor { NullaryFunctor(int state); void operator()(); private: int iState; }; Some STL algorithms requires stateless functors
Hand made functor examples Can return a value struct SomePredicate { bool operator(int i)(); };
STL algorithm example template <class InputIterator, class Predicate> std::iterator_traits<InputIterator>::difference_type std::count_if(InputIterator first, InputIterator last, Predicate pred) • InputIterator is a model of the Input Iterator concept • Predicate is a model of the Predicate concept • InputIterator's value type is convertible to Predicate's argument type
STL functor example namespace { struct MyPredicate { bool operator()(int i) const { return i>=0 && i<=9; } }; } { std::vector<int> vec = ...; unsigned int single_digits = std::count_if(vec.begin(), vec.end(), MyPredicate()); }
boost::function Generalized callback through functors • Compatible with free functions, member functions and other functors • Abstracts away callback type • Intuitive semantics • Easy to read syntax
boost::function example void free_func(int x); { boost::function<void(int x)> callback = free_func; callback(10); }
boost::function example struct MyFunctor { void operator()(int x); }; { boost::function<void(int x)> callback = MyFunctor(); callback(10); }
boost::function example struct MyClass { void member_func(int x); }; { // Need an instance to call a member function variable boost::function<void(MyClass*, int x)> callback = &MyClass::member_func; MyClass my_class; callback(&my_class, 10); }
boost::function semantics { boost::function<void(int x)> callback; // unitialized ... if (callback) callback(10); }
boost::function usage example class OkCancelDialog { OkCancelDialog(const boost::function<void()>& onOk, const boost::function<void()>& onCancel); };
Recall from RAII presentation prepare_something(); possibly_throwing_call_1(); possibly_throwing_call_2(); rollback_preparation(); rollback_call_1();
Recall from RAII presentation prepare_something(); try { possibly_throwing_call_1(); try { possibly_throwing_call_2(); } catch (...) { rollback_call_1(); rollback_preparation(); throw; } } catch (...) { rollback_preparation(); throw; }
Recall from RAII presentation • Error-prone to write • Difficult to read • Difficult to change • Poor scalability
Recall from RAII presentation class ScopeGuard { ScopeGuard(const boost::function<void()>& rollback) : mRollBack(rollback) {} ~ScopeGuard() { if (mRollBack) mRollBack(); } void Dismiss() { mRollback = boost::function<void()>(); } private: boost::function<void()> mRollback; };
Recall from RAII presentation prepare_something(); ScopeGuard preparation_guard(rollback_preparation); possibly_throwing_call_1(); ScopeGuard call_1_guard(rollback_call_1); possibly_throwing_call_2(); // Commit preparation_guard.Dismiss(); call_1_guard.Dismiss();
Writing custom made functors • Is boring • Generates a lot of code • Takes some time
boost::bind Generates unnamed functors from: • Function pointers • Member function pointers • Other functors Meant to be used as unnamed temporaries
boost::bind example void foo (int arg1, int arg2, int arg3) { std::cout << arg1 << ” ” << arg2 << ” ” << arg3 << std::endl; } { boost::bind(foo, _1, _2, _3) (1, 2, 3); boost::bind(foo, _3, _2, _1) (1, 2, 3); boost::bind(foo, _1, _1, _1) (20); boost::bind(foo, _3, _3, _3) (10, 20, 30); boost::bind(foo, 1, 2, 3) (); boost::bind(foo, 1, _1, 1) (1, 2, 3); }
boost::bind, what’s happening? void foo (int arg1, int arg2, int arg3); boost::bind(foo, 10, _1, 30); What’s the return type of the bind expression? struct some_super_strange_type_with_loads_of_templates { void operator(int x) { foo(iArg1, x, iArg3); } int iArg1, iArg3; };
boost::bind example namespace { bool my_pred (int i) { return i>=0 && i<=9; } } { std::vector<int> vec = ...; unsigned int single_digits = std::count_if(vec.begin(), vec.end(), boost::bind(my_pred, _1)); }
boost::bind logical operators { std::vector<int> vec = ...; unsigned int single_digits = std::count_if(vec.begin(), vec.end(), _1>=0 && _1<=9); }
boost::bind member functions struct X { void SomeFunc(int arg1, int arg2); }; { std::vector<X> vec; std::for_each(vec.begin(), vec.end(), boost::bind(&X::SomeFunc, _1, 1, 2)); }
boost::bind nested expressions std::string f(std::string const & x) { return "f(" + x + ")"; } std::string g(std::string const & x) { return "g(" + x + ")"; } std::string h(std::string const & x, std::string const & y) { return "h(" + x + ", " + y + ")"; } std::string k() { return "k()"; } template<class F> void test(F f) { std::cout << f("x", "y") << '\n'; } { using namespace boost; test( bind(f, bind(g, _1)) ); test( bind(f, bind(h, _1, _2)) ); test( bind(h, bind(f, _1), bind(g, _1)) ); test( bind(h, bind(f, _1), bind(g, _2)) ); test( bind(f, bind(k)) ); }
boost::bind semantics Bound values are stored by value! struct some_super_strange_type_with_loads_of_templates { void operator(int x) { foo(iArg1, x, iArg3); } int iArg1, iArg3; };
boost::bind references void foo(const X& arg); { X x; boost::bind(foo, boost::cref(x))(); }
boost::bind and shared_ptr struct X { void foo() {} }; { boost::shared_ptr<X> x; boost::bind(&X::foo, x)(); }
bind&function usage Bind • Pass as unnamed temporaries for templated functions • Bind arguments as state • Specifically bind instances to member functions • Reorder arguments Function • Pass as arguments for non-templated functions • Abstracts away function type • Store as non-templated variables • Can be uninitialized
Combining bind and function function and bind both model the functor concept Both are compatible with other functors void func(int arg1); { boost::function<void()> call = boost::bind(func, 1000); }
Combining bind and function // Non-templated higher order function void register_callback(boost::function<void()> callback); void my_callback(int source); { register_callback(boost::bind(&my_callback, 10)); }
Boost lambda Similar to bind, larger and more advanced library Pushes the limits of the C++ language using namespace boost; std::vector<int> v = ...; std::for_each(v.begin(), v.end(), if_(_1 % 2 == 0)[ cout << _1 ]); int a[5][10]; int i; std::for_each(a, a+5, for_loop(var(i)=0, var(i)<10, ++var(i), _1[var(i)] += 1)); std::for_each(v.begin(), v.end(), ( switch_statement( _1, case_statement<0>(std::cout << constant("zero")), case_statement<1>(std::cout << constant("one")), default_statement(cout << constant("other: ") << _1) ), cout << constant("\n") ) );