开发者

Bind a function against a range to make an iterating function

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.


As far as I'm aware, this doesn't exist in Boost, because it's easily reproduced with for_each and bind, for example:

function<void()> bound = bind(
    for_each<vector<int>::iterator, function<void(int)> >, 
    v1.begin(), v1.end(), func
);`

It's fairly simple. You just need to create a templated functor bind_range with a constructor that takes the binding information (i.e. the container and functor) and an operator() that applies the function to the container.

Note, however, that saving a functor like this for later use is often dangerous, because the bind_range object could end up refering to a container that no longer exists.

A quick example:

template<typename Container, typename Function>
struct bind_range {
    bind_range(Container& target, Function func) 
        : container(target), function(func) { }

    void operator()() {
        std::for_each(container.begin(), container.end(), function);
    }

    Function function;
    Container& container;
};


I don't see what the problem is. You said this could be achieved by a for_each construct in calling code. Yes, true. Then why not put that for_each construct INTO the bind_range functor itself? I mean, bind_range is going to be a struct template with operator(). In this operator you must do a for_each. Am I missing something?


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 &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,
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜