boost transform iterator and c++11 lambda
Asked Answered
A

3

12

I'm trying to use boost::adaptors::transformed by providing a c++0x lambda to the adaptor.

The following code does not compile. I'm using g++ 4.6.2 with boost 1.48.

#include <iostream>
#include <vector>

#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

using namespace std;
namespace br    = boost::range;
namespace badpt = boost::adaptors;


int main()
{  
  vector<int> a = {0,3,1,};
  vector<int> b = {100,200,300,400};

  auto my_ftor = [&b](int r)->int{return b[r];};

  cout<<*br::max_element(a|badpt::transformed(my_ftor))<<endl;
}

Any ideas on what I'm doing wrong here?

Appetence answered 1/10, 2012 at 11:27 Comment(2)
#12672872 related and mb dup.Airless
The error log is detailed. Please see link.Appetence
P
4

Well lambdas don't play nice, since they are not default constructible, which is necessary for iterators. Here is a wrapper I use for lambdas:

#define RETURNS(...) -> decltype(__VA_ARGS__) { return (__VA_ARGS__); }

template<class Fun>
struct function_object
{
    boost::optional<Fun> f;

    function_object()
    {}
    function_object(Fun f): f(f)
    {}

    function_object(const function_object & rhs) : f(rhs.f)
    {}

    // Assignment operator is just a copy construction, which does not provide
    // the strong exception guarantee.
    function_object& operator=(const function_object& rhs)
    {
        if (this != &rhs)
        {
            this->~function_object();
            new (this) function_object(rhs);
        }
        return *this;
    }

    template<class F>
    struct result
    {};

    template<class F, class T>
    struct result<F(T)>
    {
        typedef decltype(std::declval<Fun>()(std::declval<T>())) type;
    };

    template<class T>
    auto operator()(T && x) const RETURNS((*f)(std::forward<T>(x)))

    template<class T>
    auto operator()(T && x) RETURNS((*f)(std::forward<T>(x)))
};

template<class F>
function_object<F> make_function_object(F f)
{
    return function_object<F>(f);
}

Then you can just do this:

int main()
{  
  vector<int> a = {0,3,1,};
  vector<int> b = {100,200,300,400};

  cout<<*br::max_element(a|badpt::transformed(make_function_object([&b](int r)->int{return b[r];};)))<<endl;
}
Pejoration answered 1/10, 2012 at 15:22 Comment(0)
A
9

It's well known issue. Look here

http://boost.2283326.n4.nabble.com/range-cannot-use-lambda-predicate-in-adaptor-with-certain-algorithms-td3560157.html

Shortly, you should use this macro

#define BOOST_RESULT_OF_USE_DECLTYPE

for use decltype instead of boost::result_of.

Quote from here

If your compiler supports decltype, then you can enable automatic result type deduction by defining the macro BOOST_RESULT_OF_USE_DECLTYPE, as in the following example.

Airless answered 1/10, 2012 at 11:47 Comment(5)
Hi, as I understand, this is applicable to boost 1.51 +. I tried inserting the macro before any boost headers are included but it has no effect. The other solutions in the link also presume the availability of boost/range/regular.hpp which is not available in boost-1.48. Thanks for the link.Appetence
@Appetence this macro fix more errors, but there are errors, that cannot be fixed without boost::range rewriting (construct functor by default c-tor (boost version 1.48) for example will never work with closure). Don't use closures with boost::transform_iterator nowAirless
Oh well! Thanks for the input. I'll probably go with @Paul s suggestion since I really can't change the boost version now.Appetence
Be warned, as of Boost 1.55, BOOST_RESULT_OF_USE_DECLTYPE may break boost::spirit and possibly boost::fusion.Chelonian
This might be a well known issue, but it wasn't known to me. Yesterday was the first day I tried using transform_iterator, and I had a helluva time figuring out what my compiler spew was trying to tell me. Of course I assumed I was trying to do something stupid, so this answer was exactly what I needed to see.Jota
P
4

Well lambdas don't play nice, since they are not default constructible, which is necessary for iterators. Here is a wrapper I use for lambdas:

#define RETURNS(...) -> decltype(__VA_ARGS__) { return (__VA_ARGS__); }

template<class Fun>
struct function_object
{
    boost::optional<Fun> f;

    function_object()
    {}
    function_object(Fun f): f(f)
    {}

    function_object(const function_object & rhs) : f(rhs.f)
    {}

    // Assignment operator is just a copy construction, which does not provide
    // the strong exception guarantee.
    function_object& operator=(const function_object& rhs)
    {
        if (this != &rhs)
        {
            this->~function_object();
            new (this) function_object(rhs);
        }
        return *this;
    }

    template<class F>
    struct result
    {};

    template<class F, class T>
    struct result<F(T)>
    {
        typedef decltype(std::declval<Fun>()(std::declval<T>())) type;
    };

    template<class T>
    auto operator()(T && x) const RETURNS((*f)(std::forward<T>(x)))

    template<class T>
    auto operator()(T && x) RETURNS((*f)(std::forward<T>(x)))
};

template<class F>
function_object<F> make_function_object(F f)
{
    return function_object<F>(f);
}

Then you can just do this:

int main()
{  
  vector<int> a = {0,3,1,};
  vector<int> b = {100,200,300,400};

  cout<<*br::max_element(a|badpt::transformed(make_function_object([&b](int r)->int{return b[r];};)))<<endl;
}
Pejoration answered 1/10, 2012 at 15:22 Comment(0)
H
2

@ForEver's answer (#define BOOST_RESULT_OF_USE_DECLTYPE) didn't work for me. And @Paul's answer is too long (and too general). A more specific solution can be this:

#include <iostream>
#include <vector>

#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

using namespace std;
namespace br    = boost::range;
namespace badpt = boost::adaptors;


int main()
{  
  vector<int> a = {0,3,1,};
  vector<int> b = {100,200,300,400};

  struct{
     vector<int>* bP;                               //pointer, just to imitate lambda syntax...
     int operator()(int r) const{return (*bP)[r];}  //was my_ftor = [&b](int r)->int{return b[r];};
  } my_ftor{&b};                                    //...here

  cout<<*br::max_element(a|badpt::transformed(my_ftor))<<endl;
}

(It is 2016, Boost 1.58 and this is still broken. At least lambda without captures should fulfill the requirements of boost::transformed.)

If the lambda didn't have a capture (not your case) the code would be a bit simpler OR you could use:

...
int(*my_ftor)(int) = [](int r)->int{return ...;}; // function pointer default constructible and callable
cout<<*br::max_element(a|badpt::transformed(my_ftor))<<endl;
...
Horsepowerhour answered 10/5, 2016 at 8:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.