Как написать функтор boost::lambda, который возвращает новый функтор

Как я могу написать лямбда-выражение с двумя заполнителями, один для вызываемого объекта и один для аргумента функции, чтобы при предоставлении вызываемого объекта сначала возвращалась унарная функция.

В приведенном ниже примере generate должно быть лямбда-выражением с первым заполнителем для самого вызываемого объекта и вторым заполнителем для аргумента. Вызов generate(c) должен возвращать унарную функцию, в которой отсутствует только аргумент вызова функции. На самом деле он каким-то образом уже возвращает тип bool, что подтверждается статическим утверждением.

#include <boost/lambda/bind.hpp>

struct Arg {
};

struct Callable : std::unary_function<Arg, bool> {
    bool operator()( Arg const& a ) const { return true; }
};

int main( int argc, const char* argv[] ) {
    BOOST_AUTO(generate, boost::lambda::bind(boost::lambda::_1, boost::lambda::protect(boost::lambda::_1)));

    Callable c;
    BOOST_AUTO(fn, generate(c));

    BOOST_STATIC_ASSERT((boost::is_same<BOOST_TYPEOF(fn), bool>::value));
    Arg a;
    bool b = fn(a);
    _ASSERT(b==true);
}

person Sebastian    schedule 09.03.2011    source источник


Ответы (3)


Если бы вы использовали Boost.Phoenix, ответ был бы немного проще:

#include <boost/phoenix/phoenix.hpp>

struct callable
{
    typedef bool result_type;

    bool operator()(int) const
    {
        return true;
    }
};

int main()
{
    using phx::bind;
    using phx::lambda;
    using phx::arg_names::_1;
    using phx::local_names::_a;

    auto generate = lambda(_a = _1)[bind(_a, _1)];
    auto fn = generate(callable());

    bool b = fn(8);
}

Не то чтобы это решение было более общим, чем версия, опубликованная OT. Его можно использовать с любым унарным функциональным объектом, независимо от аргумента и типа возвращаемого значения.

Минус, нужно использовать текущий наддув ствола...

person Thomas Heller    schedule 10.03.2011
comment
Я буду помнить об этом на случай, если мы постепенно введем boost::phoenix в нашу кодовую базу. boost::bind, boost::lambda::bind, boost::phoenix::bind -- это немного выходит из-под контроля, не так ли? :-) - person Sebastian; 11.03.2011
comment
Это. Феникс пытается объединить эти три. Например, phoenix::bind может выполнять все то же, что и boost::bind (он проходит все тестовые случаи из Boost.Bind), с добавлением дополнительных функциональных возможностей. - person Thomas Heller; 14.03.2011

Я решил свою проблему, хотя и не так элегантно, как надеялся:

   struct FCreateBind {
        typedef boost::_bi::bind_t<bool, Callable, boost::_bi::list2<boost::arg<1>, boost::arg<2> >  > result_type;
        result_type operator()( Callable const& c ) const {
            return boost::bind<bool>(c, _1);
        }
};
BOOST_AUTO(generate, boost::bind(FCreateBind(), _1));

   BOOST_AUTO(fn, generate(Callable());
   bool b = fn(Arg());

Конечно, в этом простом примере я мог бы просто написать BOOST_AUTO(generate, boost::lambda_1), так как Callable сам является вызываемым объектом. Но я искал способ заранее установить аргументы Callable, чтобы сгенерированная функция fn была нулевой функцией. Это решение позволит мне сделать это внутри FCreateBind.

FCreateBind, наверное, тоже можно исключить, но я пока не придумал, как определить указатель на перегруженную глобальную функцию boost::bind.

person Sebastian    schedule 10.03.2011

Хотя я не уверен на 100%, что понимаю вопрос, следующий код может соответствовать вашей цели:

template< class R >
struct FCreateBind {
  typedef boost::function< R() > result_type;

  template< class T, class U >
  result_type operator()( T const& x, U const& y ) const {
    return boost::bind( x, y );
  }
};

int main() {
  BOOST_AUTO( generate, boost::bind( FCreateBind< bool >(), Callable(), _1 ) );
  BOOST_AUTO( fn, generate( Arg() ) );
  bool b = fn();
}

При этом, вероятно, это не так красиво, как ожидает спрашивающий...
Как вы упомянули, если мы укажем одну из перегрузок boost::bind явно, FCreateBind не понадобится. Однако, насколько я видел, переносимого способа указать перегрузку не существует. Итак, в этом случае, вероятно, мы должны полагаться на внутреннюю часть boost.
К вашему сведению, во время моего тестирования мог быть скомпилирован следующий код:

int main() {
  namespace bb = boost::_bi; // Sorry, for brevity
  bb::bind_t< bb::unspecified, Callable, bb::list1< bb::value< Arg > > >
    (*bi)( Callable, Arg ) = boost::bind< bb::unspecified, Callable, Arg >;
  BOOST_AUTO( generate, boost::bind( bi, Callable(), _1 ) );
  BOOST_AUTO( fn, generate( Arg() ) );
  bool b = fn();
}

Надеюсь это поможет

person Ise Wisteria    schedule 10.03.2011
comment
Первое решение в значительной степени то, что я предложил сам в своем собственном ответе, но с использованием boost:: function в качестве результата_типа. Всякий раз, когда можно использовать явный тип, я предпочитаю его обёртке функтора в boost::function. Спасибо, однако, за то, что выяснили, как записать функтор boost::bind. - person Sebastian; 11.03.2011