将范围内的函数绑定以生成迭代函数

时间:2011-03-31 09:16:35

标签: c++ templates boost foreach boost-bind

我正在尝试实现可以​​绑定范围的我自己的bind_range。它应该允许这样的客户端代码:

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

在上面的代码中,我的模板bind_range检测到v1符合ForwardRangeConcept<>,并且其值类型与f()的第一个参数兼容。然后它生成一个函数对象,它将迭代v1,为每个值调用f()

我知道上面的内容可以通过调用代码中的某种形式的for-each构造来实现,但是我想接受绑定函数并在以后使用它。

以上是我想要实现的目标。我已阅读C++ Template Metaprogramming的副本并查看boost::bind实施,但我无法开始使用解决方案。我也有一种唠叨的感觉,这种类似的东西已经存在于Boost库的某个地方。

扩展

绑定多个范围。例如:

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

处理返回类型。我不需要迭代调用的返回类型,但可以想象有人可能想要存储或处理每个返回值。我确信这可以用某种Lamda型结构巧妙地完成。

3 个答案:

答案 0 :(得分:3)

据我所知,这在Boost中不存在,因为它可以使用for_eachbind轻松复制,例如:

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

这很简单。您只需要创建一个模板化仿函数bind_range,其中包含一个获取绑定信息的构造函数(即容器和仿函数)和一个将该函数应用于容器的operator()

但请注意,保存这样的仿函数以供以后使用通常很危险,因为bind_range对象最终可能会引用不再存在的容器。

一个简单的例子:

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

答案 1 :(得分:0)

我看不出问题所在。你说这可以通过调用代码中的for_each构造来实现。是的,没错。那么为什么不把for_each构造INTO到bind_range函子本身呢?我的意思是,bind_range将是一个带有operator()的结构模板。在此运算符中,您必须执行for_each。我错过了什么吗?

答案 2 :(得分:0)

我对此做了一点点,这就是我想出来的。它工作,但是不完整,因为只有模板用于一元函数和二元函数,其中范围绑定到第一个参数。如果有人进一步采取这种做法并使其更通用,请在此处回复。

#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,
}