[Boost.Range] combine, native range-for, and destructuring

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

[Boost.Range] combine, native range-for, and destructuring

Boost - Users mailing list
I want to go through two vectors at the same time, and the way to do this is to "zip" the
items from each into a tuple.  boost::combine is a function for that.

     for (auto&[def,residue] : boost::combine(playdef, residues)) {

gives me an error

1>e:\scratch\c++code\scratchproject2\fizzbuzz\fizzbuzz.cpp(66): error C2440:
'initializing': cannot convert from

'boost::tuples::cons<std::pair<int,std::string_view> &,boost::tuples::cons<int
&,boost::fusion::detail::build_tuple_cons<boost::fusion::single_view_iterator<Sequence,boost::mpl::int_<1>>,Last,true>::type>>'
to

'boost::tuples::cons<std::pair<int,std::string_view> &,boost::tuples::cons<int
&,boost::fusion::detail::build_tuple_cons<boost::fusion::single_view_iterator<Sequence,boost::mpl::int_<1>>,Last,true>::type>>
&'


The only difference is in the final `&` character.

Trying to explore the behavior in pieces, I try:

     for (auto row : boost::combine(playdef, residues)) {
      auto&[def, residue] = row;  // line 1
      row.get<1>() = 17;  // line 2
      residue= 42;  // line 3

Now line2 does work, and I note that assignment goes through and updates the original
`residues` vector.

The destructuring on line 1 does compile now, being on a separate line.
But line 3 gives me an error about not being able to assign to a crazy complicated type.

What's going on?  Is Boost.Range zipped ranges simply not compatible with the native
language features?

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [Boost.Range] combine, native range-for, and destructuring

Boost - Users mailing list
On Sat, 14 Apr 2018 at 08:02 John M. Dlugosz via Boost-users <[hidden email]> wrote:
I want to go through two vectors at the same time, and the way to do this is to "zip" the
items from each into a tuple.  boost::combine is a function for that.

     for (auto&[def,residue] : boost::combine(playdef, residues)) {

gives me an error



<snip>


Trying to explore the behavior in pieces, I try:

     for (auto row : boost::combine(playdef, residues)) {
        auto&[def, residue] = row;  // line 1
        row.get<1>() = 17;  // line 2
        residue= 42;  // line 3

Now line2 does work, and I note that assignment goes through and updates the original
`residues` vector.

The destructuring on line 1 does compile now, being on a separate line.
But line 3 gives me an error about not being able to assign to a crazy complicated type.

What's going on?  Is Boost.Range zipped ranges simply not compatible with the native
language features?

Hi John,

I'm not en expert on this library, but I have run into the first of your problems before.

When dereferencing a normal iterator, the iterator returns a reference to the actual object being pointed to. The zip iterator can not do this, instead it returns a tuple of references to the the underlying objects as a temporary.

Consider:

vector<int> i;
vector<float> j;

*boost::range::combine(i, j); // This must return something like tuple<int&, float&>

So, you need to bind whats returned from combine to either a const& or an rvalue ref.

for (auto&& row : boost::combine(playdef, residues))

or

for (auto const& row : boost::combine(playdef, residues))

Even the const will work because it's the tuple that's const, not the objects being referenced. As an aside, this means that zip_iterator is not, and can not be, a "proper" iterator as far as standard C++ is concerned. If I remember correctly, this is one issue that the ranges proposal is trying to fix.

As for the second problem, this looks to me like a bug as the library is not correctly interacting with C++17 structured bindings. This  should not really be a surprise, considering this library is much older than that language feature, but it should be addressed, so log a bug, or have a go at trying to fix it and submit a pull request.

Hope this helps,

-- chris


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [Boost.Range] combine, native range-for, and destructuring

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
On 14 April 2018 at 04:34, John M. Dlugosz via Boost-users <[hidden email]> wrote:
I want to go through two vectors at the same time, and the way to do this is to "zip" the items from each into a tuple.  boost::combine is a function for that.
 
I've run into this problem as well, it's a real shame it doesn't work, because if you would be able to use boost::combine with (C++17) structured bindings, one can completely abstract away (if it would work) the zip-iterator (you wouldn't see it even though that's what's happening under the hood)...

degski

PS: I did not report it because I wasn't sure the problem was not related to the MS-implementation of structured bindings (this is a while ago), and I did not want to attract anymore flak for being a ms-boy. It seems it isn't then.


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [Boost.Range] combine, native range-for, and destructuring

Boost - Users mailing list
On 14 April 2018 at 17:49, degski <[hidden email]> wrote:
On 14 April 2018 at 04:34, John M. Dlugosz via Boost-users <[hidden email]> wrote:
I want to go through two vectors at the same time, and the way to do this is to "zip" the items from each into a tuple.  boost::combine is a function for that.
 
I've run into this problem as well, it's a real shame it doesn't work, because if you would be able to use boost::combine with (C++17) structured bindings, one can completely abstract away (if it would work) the zip-iterator (you wouldn't see it even though that's what's happening under the hood)...

The construct does work with std:tie, but that's at least 3 more lines of code and does not look as "sexy" as the (not working) structured bindings approach (sorry for the nerdy "does not look sexy" comment).
 
degski

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [Boost.Range] combine, native range-for, and destructuring

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
John M. Dlugosz via Boost-users wrote:
>      for (auto&[def,residue] : boost::combine(playdef, residues)) {
>
> gives me an error

This fails, because the deref operator of zip_iterator generates
a prvalue (of a tuple of references). Instead, you can use
- for (auto [x, y] : combine(...)) {...}
- for (auto const& [x, y] : combine(...)) {...}
- for (auto&& [x, y] : combine(...)) {...}


>      for (auto row : boost::combine(playdef, residues)) {
>       auto&[def, residue] = row;  // line 1
>       row.get<1>() = 17;  // line 2
>       residue= 42;  // line 3
>
> Now line2 does work, and I note that assignment goes through and updates the
> original
> `residues` vector.
>
> The destructuring on line 1 does compile now, being on a separate line.
> But line 3 gives me an error about not being able to assign to a crazy
> complicated type.

Hmm..., with boost (develop branch) + GCC 7.3 or Clang trunk,
the following code runs fine:

    std::vector<int> v1{1, 1, 1};
    std::vector<int> v2{2, 2, 2};

    for (auto&& tpl : boost::range::combine(v1, v2)) {
        auto [x, y] = tpl; // `auto&`, `auto const&` and `auto&&` also work fine
        x = 10;
        y = 20;
    }

    for (auto&& [x, y] : boost::range::combine(v1, v2)) {
        x = 200;
        y = 200;
    }


Regards,
Michel
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [Boost.Range] combine, native range-for, and destructuring

Boost - Users mailing list

On Tue, 17 Apr 2018 at 11:00 Michel Morin via Boost-users <[hidden email]> wrote:
 
Hmm..., with boost (develop branch) + GCC 7.3 or Clang trunk,
the following code runs fine:

    std::vector<int> v1{1, 1, 1};
    std::vector<int> v2{2, 2, 2};

    for (auto&& tpl : boost::range::combine(v1, v2)) {
        auto [x, y] = tpl; // `auto&`, `auto const&` and `auto&&` also work fine
        x = 10;
        y = 20;
    }

    for (auto&& [x, y] : boost::range::combine(v1, v2)) {
        x = 200;
        y = 200;
    }


 FWIW, I just checked, and this works in the develop, but not master or 1.67.

-- chris

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users