Higher order programming

advertisement
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++ example concepts
Concept
Assumed valid statment
Default constructible
T t;
Copyable
T t2(t1);
Assignable
t1 = t2;
Convertible to OtherType
static_cast<OtherType>(t);
Output streamable
stream << t;
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,
boost::bind(foo,
boost::bind(foo,
boost::bind(foo,
boost::bind(foo,
boost::bind(foo,
}
_1, _2, _3) (1, 2, 3);
_3, _2, _1) (1, 2, 3);
_1, _1, _1) (20);
_3, _3, _3) (10, 20, 30);
1, 2, 3) ();
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") )
);
Download