Привязать функцию к диапазону, чтобы создать итерационную функцию - PullRequest
3 голосов
/ 31 марта 2011

Я пытаюсь реализовать свой собственный 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 ]

3 голосов
/ 31 марта 2011

Насколько я знаю, в Boost этого нет, потому что его легко воспроизвести с помощью for_each и bind, например:

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;
};
0 голосов
/ 01 апреля 2011

Я немного поработал над этим, и вот что я придумал. Это работает, но не является полным, поскольку есть только шаблоны для унарных функций и бинарных функций, где диапазон связан с первым параметром. Если кто-то идет дальше и делает его более общим, пожалуйста, напишите сюда.

#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 голосов
/ 31 марта 2011

Я не вижу, в чем проблема. Вы сказали, что это может быть достигнуто конструкцией for_each в вызывающем коде. Да, верно. Тогда почему бы не поместить эту конструкцию for_each в сам функтор bind_range? Я имею в виду, что bind_range будет структурным шаблоном с оператором (). В этом операторе вы должны сделать for_each. Я что-то упустил?

...