I am trying to implement my own bind_range
that can bind against a range. It should allow client code like this:
void f(int x, int y)
{
std::cout << x + y << ',';
}
std::vector<int> v1; // contains 1,2,3
void go()
{
boost::function<void(int y)> f_bound = bind_range(f, v1, _1);
f_bound(10); // prints 11,12,13,
}
In the above code, my templated bind_range
detects that v1
conforms to ForwardRangeConcept<>
and that its value type is compatible with f()
's first parameter. It then generates a function object that will iterate over v1
, calling f()
for each value.
I know that the above could be achieved with some form of for-each construct in the calling code but I want to take the bound function and use it later.
The above is the essence of what I want to achieve. I have read my copy of C++ Template Metaprogramming and looked at the boost::bind
implementation but I can't get started with a solution. I also have the nagging feeling that something like this already exists already somewhere in the Boost libraries.
Extensions:
Binding multiple ranges. For example:
std::vector<int> v10; // contains 10,20,30
void go_multiple()
{
boost::function<void()> f_bound = bind_range(f, v10, v1);
f_bound(); // prints 11,12,13,21,22,23,31,32,33,
}
Dealing with return type. I don't need a return type from my iterated calls but it's conceivable that someone might want to store or process each return value. I'm sure this could be done neatly with some sort of Lamda-type construct.
I worked on this a little and here is what I came up with. It works, but is incomplete as there are only templates for unary functions and binary functions where the range is bound to the first parameter. If anyone takes this further and makes it more generic, please do post back here.
#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <boost/range/value_type.hpp>
#include <boost/type_traits/function_traits.hpp>
#include <boost/foreach.hpp>
template <class Range>
struct GetRangeValue
{
typedef typename boost::range_value<Range>::type Value;
};
template <class Function, class Range>
struct BindForEachBase
{
BindForEachBase(Function *f, Range &r) : function(f), range(r) { }
Function *const function;
Range ⦥
};
template <class Function, class Range>
struct BindForEach1
: BindForEachBase<Function, Range>
{
BindForEach1(Function *f, Range &r)
: BindForEachBase(f, r)
{ }
void operator()()
{
BOOST_FOREACH(GetRangeValue<Range>::Value v, range) (*function)(v);
}
};
template <class Function, class Range>
BindForEach1<Function, Range>
bindForEach(Function *f, Range &r)
{
return BindForEach1<Function, Range>(f, r);
}
template <class Function, class Range, class A1>
struct BindForEach2
: BindForEachBase<Function, Range>
{
BindForEach2(Function *f, Range &r)
: BindForEachBase(f, r)
{ }
void operator()(A1 a1)
{
boost::function1<void, GetRangeValue<Range>::Value> f(boost::bind(*this->function, _1, a1));
bindForEach(&f, range)();
}
};
template <class Function, class Range, class Placeholder1>
typename boost::enable_if
<
boost::is_placeholder<Placeholder1>,
BindForEach2<Function, Range, typename boost::function_traits<Function>::arg2_type>
>::type
bindForEach(Function *f, Range &r, Placeholder1 p1)
{
return BindForEach2<Function, Range, boost::function_traits<Function>::arg2_type >(f, r);
}
void f(int x, int y)
{
std::cout << x + y << ',';
}
#include <boost/assign/std/vector.hpp>
#include <vector>
using namespace boost::assign;
void go()
{
std::vector<int> v1;
v1 += 1,2,3;
boost::function<void(int y)> f_bound = bindForEach(f, v1, _1);
f_bound(10); // prints 11,12,13,
}