Boost.Bind и композиция функторов
От: adalekin Россия IT Rightway
Дата: 08.06.10 19:06
Оценка:
Доброго времени суток, коллеги!

Хочется странного, а именно вычисления placeholder'ов (_1, _2 и т.д.) без композиции функторов. Проблема поднималась очень давно здесь, но решения так никто и не предложил. Ниже приводится законченный пример, который иллюстрирует ситуацию:

#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <boost/function.hpp>

#include <iostream>

class f_runner
{
public:
    void run(boost::function<void ()> f, bool deferred)
    {
        std::cout << "From runner " << (deferred ? "( deferred ): " : ": ");
        
        f();
    }
};

class h
{
public:
    void handle(const std::string & a, const std::string & b)
    {
        std::cout << a << " - " << b;
    }
};

template<class T>
void wrapper(T f)
{
    f("a", "b");
}

int main(int argc, const char * const argv[])
{
    f_runner runner;

    h handler;

    // What do we really need:
    // runner.run(boost::bind(f, "arg1", "arg2"), true);

    boost::function<void (const std::string &, const std::string &)> f =\
        boost::bind(&h::handle, boost::ref(handler)
            , _1
            , _2);

    boost::function<void (const std::string &, const std::string &)> run =\
        boost::bind(&f_runner::run, boost::ref(runner)
            , boost::protect(boost::bind(f
                , _1
                , _2))    // evaluation of _1 and _2 is needed
            , true);
    
    wrapper(run);
    
    return 0;
}


В выделенном фрагменте используется boost::protect, чтобы предотвратить композицию и для метода f_runner::run. Однако boost::protect предотвращает вычисление placeholder'ов. Есть ли какие-нибудь пути обхода сложившейся ситуации?
boost bind composition композиция функтор functor
Re: Boost.Bind и композиция функторов
От: XuMuK Россия  
Дата: 09.06.10 08:09
Оценка:
Здравствуйте, adalekin, Вы писали:

A>Доброго времени суток, коллеги!


A> // What do we really need:

A> // runner.run(boost::bind(f, "arg1", "arg2"), true);
A>


#include <tr1/functional>
#include <iostream>

class f_runner
{
public:
    void run(std::tr1::function<void ()> f, bool deferred)
    {
        std::cout << "From runner " << (deferred ? "( deferred ): " : ": ");
        f();
    }
};

class h
{
public:
    void handle(const std::string & a, const std::string & b)
    {
        std::cout << a << " - " << b;
    }
};

int main(int argc, const char * const argv[])
{
    f_runner runner;
    h handler;
    std::tr1::function<void (const std::string &, const std::string &)> f = std::tr1::bind(&h::handle, std::tr1::ref(handler)
                , std::tr1::placeholders::_1 , std::tr1::placeholders::_2);
    runner.run(std::tr1::bind(f, "arg1", "arg2"), true);
    return 0;
}


отлично компилируется и работает как задумано: http://codepad.org/rka47j4d
Re[2]: Boost.Bind и композиция функторов
От: adalekin Россия IT Rightway
Дата: 09.06.10 09:07
Оценка:
Здравствуйте, XuMuK, Вы писали:

XMK> отлично компилируется и работает как задумано: http://codepad.org/rka47j4d


Никто не сомневается, что bind компилируется и работает. Речь идет именно о композиции функторов.

Комментарий

// What do we really need:
// runner.run(boost::bind(f, "arg1", "arg2"), true)


показывает порядок функторов, который хочется иметь. Возможно, я недостаточно точно описал суть проблемы.

Есть wrapper, который вызывает функтор с двумя параметрами. Есть 2 функтора:

Если wrapper вызывает f:

boost::function<void (const std::string &, const std::string &)> f =\
    boost::bind(&h::handle, boost::ref(handler)
        , _1
        , _2);

wrapper(f);


все здорово и тривиально (как в описанном Вами примере). Однако сложность заключается в том, чтобы "обернуть" функтор f в функтор run.

boost::function<void (const std::string &, const std::string &)> f =\
    boost::bind(&h::handle, boost::ref(handler)
        , _1
        , _2);

    boost::function<void (const std::string &, const std::string &)> run =\
        boost::bind(&f_runner::run, boost::ref(runner)
         , boost::protect(boost::bind(f
                , _1
                , _2))    // evaluation of _1 and _2 is needed
        , true);
    
wrapper(run);
avalon 1.0rc2 rev 272
Re: Boost.Bind и композиция функторов
От: Кодт Россия  
Дата: 09.06.10 19:05
Оценка: 2 (1)
Здравствуйте, adalekin, Вы писали:

A>Доброго времени суток, коллеги!


A>Хочется странного, а именно вычисления placeholder'ов (_1, _2 и т.д.) без композиции функторов.


По сути, нужно забиндить функцию bind.
void foo(string a, string b, string c) { cout<<a<<b<<c; }
void bar(function<void()> f, bool b) { cout<<b; f(); }
void wrapper(function<void(string,string)> f) { f("a","b"); }

foo("a","+","b");
wrapper(bind(foo,_1,"+",_2));

bar(bind(foo,"b","+","a"),true);
wrapper(bind(bar,bind(bind,foo,_2,"+",_1),true);


Поскольку bind — перегруженная функция, возвра, и вообще там чёрт ногу сломит с типами, я решил сделать проще: написать своё
template<class R>
struct metabind
{
    typedef R result_type;
        
    template<class A1>
    result_type operator()(A1 a1) const { return bind(a1); }
    
    template<class A1, class A2>
    result_type operator()(A1 a1, A2 a2) const { return bind(a1,a2); }
    
    template<class A1, class A2, class A3>
    result_type operator()(A1 a1, A2 a2, A3 a3) const { return bind(a1,a2,a3); }
    
    template<class A1, class A2, class A3, class A4>
    result_type operator()(A1 a1, A2 a2, A3 a3, A4 a4) const { return bind(a1,a2,a3,a4); }
    
    template<class A1, class A2, class A3, class A4, class A5>
    result_type operator()(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) const { return bind(a1,a2,a3,a4,a5); }
};


wrapper(bind(bar,bind(metabind<function<void(string,string)>(),foo,_2,"+",_1),true);

Теоретически, этот metabind там и так есть. Вопрос в том, как его правильно объявить
Перекуём баги на фичи!
Re[2]: Boost.Bind и композиция функторов
От: adalekin Россия IT Rightway
Дата: 09.06.10 21:35
Оценка:
Здравствуйте, Кодт, Вы писали:

К> По сути, нужно забиндить функцию bind.


bind(bar
    , bind(metabind<function<void(string,string)> >()
        , foo
        , _2
        , "+"
        , _1)
    , true)("a", "b");


В теории все выглядит очень логично, даже эллегантно. Композиция bar и функтора, который вернет metabind, есть ровно то, что нужно — вычисляются _1 и _2, а bar получает функтор в качестве параметра.

Но заставить скомпилироваться этот код мне так и не удалось. MSVC++ в итоге отчаянно пытается увидеть во всей конструкции функтор без параметров.

error C2064: term does not evaluate to a function taking 0 arguments


Аналогичную ошибку выдает и gcc 4.4.3.

Похоже, что реализация bind к таким маневрам не приспособлена. Но сама идея "забиндить функцию bind" просто "ух"! Огромное спасибо!
avalon 1.0rc2 rev 272
Re[3]: Boost.Bind и композиция функторов
От: Кодт Россия  
Дата: 09.06.10 22:17
Оценка: 6 (2)
Здравствуйте, adalekin, Вы писали:

A>Но заставить скомпилироваться этот код мне так и не удалось. MSVC++ в итоге отчаянно пытается увидеть во всей конструкции функтор без параметров.


A>Похоже, что реализация bind к таким маневрам не приспособлена. Но сама идея "забиндить функцию bind" просто "ух"! Огромное спасибо!


Всё приспособлено, только оно, как женщины и тетрис на девятом уровне: не прощают ошибок.
Надо было правильно тип указывать...

http://codepad.org/xOBA7HrF
#include <boost/bind.hpp>
#include <boost/function.hpp>
using namespace boost;

template<class R>
struct metabind
{
    typedef R result_type;
        
    template<class A1>
    result_type operator()(A1 a1) const { return bind(a1); }
    
    template<class A1, class A2>
    result_type operator()(A1 a1, A2 a2) const { return bind(a1,a2); }
    
    template<class A1, class A2, class A3>
    result_type operator()(A1 a1, A2 a2, A3 a3) const { return bind(a1,a2,a3); }
    
    template<class A1, class A2, class A3, class A4>
    result_type operator()(A1 a1, A2 a2, A3 a3, A4 a4) const { return bind(a1,a2,a3,a4); }
    
    template<class A1, class A2, class A3, class A4, class A5>
    result_type operator()(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) const { return bind(a1,a2,a3,a4,a5); }
};

void foo(string a, string o, string b) { cout<<a<<o<<b<<endl; }

void bar(function<void()> f, string c) { cout<<c; f(); }

void wrapper(function<void(string,string)> f) { f("alfa","beta"); }


int main()
{
  wrapper(
    bind(
      bar, // bar(function<void()>, string)
      bind(
        metabind< function<void()> >(),
        foo, _2, " + ", _1 // эти местоимения относятся к внешнему бинду, метабинд получит значения
      ),
      "go "
    )
  ); // вывод: go beta + alfa
}


Извиняюсь, что изначально написал ерунду. Сначала всё у себя сделал правильно, а потом решил не копипастить, а по памяти наколотить.
Перекуём баги на фичи!
Re[4]: Boost.Bind и композиция функторов
От: adalekin Россия IT Rightway
Дата: 10.06.10 06:57
Оценка:
Здравствуйте, Кодт, Вы писали:

К> Всё приспособлено, только оно, как женщины и тетрис на девятом уровне: не прощают ошибок.

К> Надо было правильно тип указывать...

Блин, как я этого не увидел
avalon 1.0rc2 rev 272
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.