Boost logo

Boost :

Subject: Re: [boost] [Bind] How do I get bind to deduce the return type offor_each?
From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2010-04-16 16:33:56


On Fri, Apr 16, 2010 at 3:48 PM, Peter Dimov <pdimov_at_[hidden]> wrote:
> Thomas Jordan wrote:
>>
>> Thanks a lot for the suggestion, I did take a look at Lambda library, and
>> I saw that it used some extra type information in its STL
>> algorithm wrappers (the 'sig') to help it figure out the return types.
>> However, I am against using the Lambda library
>> for other reasons, I only want to use the bind library.  I'd appreciate
>> some specific advice on how to get the type deduction
>> with the Boost.bind library.  Or is this just not possible with
>> Boost.bind?
>
> It's not. Boost.Bind only supports function objects with a specific, fixed
> type. There is no mechanism to tell it that the return type of
>
> struct myForEach
> {
>  template <typename II, typename F>
>  F operator()(II begin, II end, F function) const
>  {
>     return std::for_each(begin, end, function);
>  }
> };
>
> is the same as its third argument. In this specific case I'd just use
>
> struct myForEach
> {
>  typedef void result_type;
>  template <typename II, typename F>
>  void operator()(II begin, II end, F function) const
>  {
>     std::for_each(begin, end, function);
>  }
> };

That should work fine, but I was just playing around with this as
well, so I'll just share some alternatives to consider.

It seems Thomas wants to iterate over the objects in a
multidimensional vector while calling a member function on each one.
Before getting to boost bind, first, let's consider boost lambda,
which has special facilities for embedded STL algorithms. For example,
the following iterates over a 2x2 matrix containing objects of type S
and calls S::f() on each one.

#include <algorithm>
#include <vector>
#include <boost/lambda/algorithm.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/range.hpp>
using namespace boost;

struct S
{
    int f() const {return 0;}
};

int main()
{
    typedef std::vector<S> vector_type;
    typedef std::vector<vector_type> matrix_type;
    matrix_type a(2, vector_type(2));

    { using namespace boost::lambda;
      std::for_each(a.begin(), a.end(),
          bind(lambda::ll::for_each(),
               bind(begin<const vector_type>, _1),
               bind(end<const vector_type>, _1),
               protect(bind(&S::f, _1))
          )
      );
    }
}

This is adapted from the example code (http://tinyurl.com/y7jhu8t) in
the lambda documentation. However, the same code can work using
boost::bind if you change the block with the for_each call to

    {
      std::for_each(a.begin(), a.end(),
          bind<void>(lambda::ll::for_each(),
               bind(begin<const vector_type>, _1),
               bind(end<const vector_type>, _1),
               protect(bind(&S::f, _1))
          )
      );
    }

Note the bind<void>. This overrides bind's type deduction, which
doesn't matter in this case, since the result of for_each is ignored
anyway. Now, if you needed the result, to my knowledge, TR1 bind can
handle argument dependent return types via result_of. However, boost
lambda doesn't currently support the TR1 result_of protocol. See Trac
ticket 864. So, you would still need to roll your own for_each functor
with TR1 bind.

Out of curiosity I tried this in c++0x. The following works with g++ 4.5.

#include <algorithm>
#include <vector>
#include <functional>
#include <boost/lambda/algorithm.hpp>
#include <boost/range.hpp>
using namespace boost;

struct S
{
    int f() const { return 0; }
};

int main()
{
    typedef std::vector<S> vector_type;
    typedef std::vector<vector_type> matrix_type;
    matrix_type a(2, vector_type(2));

    { using namespace std;
      using namespace std::placeholders;
      for_each(a.begin(), a.end(),
          bind(lambda::ll::for_each(),
               bind(begin<const vector_type>, _1),
               bind(end<const vector_type>, _1),
               [](S x) { x.f(); }
          )
      );
    }
}

Note that std::bind correctly deduces the result type of
lambda::ll::for_each due to its use of a decltype-based
std::result_of... Or you can just skip bind altogether...

    { using namespace std;
      for_each(a.begin(), a.end(),
          [](vector_type b) {
              for_each(b.begin(), b.end(), [](S x){ x.f(); });
          }
      );
    }

Ah, that's nice. :)

Daniel Walker


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk