[fusion] iterating over a sequence with non-const references

Hi, I would like to iterate over a sequence and preserve the reference/const/non-const of the elements. That is say I have a sequence like this: tuple<int&, int> seq; I would like to this to be true: typeof(deref(begin(seq))) == int& typeof(deref(next(begin(seq)))) == int&& It seems fusion sequences always return a const reference to the element. Is there a workaround to get it to work like this? Or is this just not possible with Boost.Fusion? Furthermore, I would like to use the transformation algorithms (especially push_back, and push_front) and still preserve the reference type. Is that possible, through some workarounds? Finally, would it still be fairly efficient when dealing with references, to copy the tuple and make a new tuple when doing algorithms such as push_back or push_front? Or is the lazy evaluation much more efficient than that? Thanks, Paul Fultz II

on Sat Mar 31 2012, paul Fultz <pfultz2-AT-yahoo.com> wrote:
Hi, I would like to iterate over a sequence and preserve the
reference/const/non-const of the elements. That is say I have a sequence like this:
tuple<int&, int> seq;
I would like to this to be true:
typeof(deref(begin(seq))) == int& typeof(deref(next(begin(seq)))) == int&&
It seems fusion sequences always return a const reference to the element. Is there a workaround to get it to work like this?
Trust me, you don't want that, for roughly the same reason you don't want regular sequence rvalues to have rvalue-returning iterators: https://groups.google.com/d/msg/comp.lang.c++.moderated/1BwNTHCFiOs/L1T3WC9T...
Or is this just not possible with Boost.Fusion?
I think you'd need to create your own iterator fusion-style iterator for this purpose.
Furthermore, I would like to use the transformation algorithms (especially push_back, and push_front) and still preserve the reference type.
Sorry, I don't know what that means. How could using an algorithm change a sequence's (or iterator's?) reference type?
Is that possible, through some workarounds? Finally, would it still be fairly efficient when dealing with references, to copy the tuple and make a new tuple when doing algorithms such as push_back or push_front? Or is the lazy evaluation much more efficient than that?
This part is best answered by others. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Hi, I would like to iterate over a sequence and preserve the
reference/const/non-const of the elements. That is say I have a sequence like this:
tuple<int&, int> seq;
I would like to this to be true:
typeof(deref(begin(seq))) == int& typeof(deref(next(begin(seq)))) == int&&
It seems fusion sequences always return a const reference to the element. Is there a workaround to get it to work like this?
Trust me, you don't want that, for roughly the same reason you don't want regular sequence rvalues to have rvalue-returning iterators: https://groups.google.com/d/msg/comp.lang.c++.moderated/1BwNTHCFiOs/L1T3WC9T...
I can see how that would be bad. Im using it to invoke functions which wouldn't be a problem for that, but other use cases would cause problems. Right now, it won't return a non-const reference from the iterator for tuples.
Or is this just not possible with Boost.Fusion?
I think you'd need to create your own iterator fusion-style iterator for this purpose.
Furthermore, I would like to use the transformation algorithms (especially push_back, and push_front) and still preserve the reference type.
Sorry, I don't know what that means. How could using an algorithm change a sequence's (or iterator's?) reference type?
Well for example in push_back, it adds const to the sequence like this: result_of::push_back<Sequence const, T> Which will cause all references in the sequence to become const, as well. Thanks, for the reply, but im thinking about just implementing an invoke that works just for tuples, but multiple tuples can be given. That way I won't need to use push_back and push_front. I will just need to find a way to bring together all the tuples before the invoke.

On 4/2/2012 3:56 AM, paul Fultz wrote:
Hi, I would like to iterate over a sequence and preserve the
reference/const/non-const of the elements. That is say I have a sequence like this:
tuple<int&, int> seq;
I would like to this to be true:
typeof(deref(begin(seq))) == int& typeof(deref(next(begin(seq)))) == int&&
It seems fusion sequences always return a const reference to the element. Is there a workaround to get it to work like this?
Trust me, you don't want that, for roughly the same reason you don't want regular sequence rvalues to have rvalue-returning iterators: https://groups.google.com/d/msg/comp.lang.c++.moderated/1BwNTHCFiOs/L1T3WC9T...
I can see how that would be bad. Im using it to invoke functions which wouldn't be a problem for that, but other use cases would cause problems. Right now, it won't return a non-const reference from the iterator for tuples.
Or is this just not possible with Boost.Fusion?
I think you'd need to create your own iterator fusion-style iterator for this purpose.
Furthermore, I would like to use the transformation algorithms (especially push_back, and push_front) and still preserve the reference type.
Sorry, I don't know what that means. How could using an algorithm change a sequence's (or iterator's?) reference type?
Well for example in push_back, it adds const to the sequence like this:
result_of::push_back<Sequence const, T>
Which will cause all references in the sequence to become const, as well.
Thanks, for the reply, but im thinking about just implementing an invoke that works just for tuples, but multiple tuples can be given. That way I won't need to use push_back and push_front. I will just need to find a way to bring together all the tuples before the invoke.
push_back is designed to be non-mutating. That is why. This is true of all the views returned from 'algos', by design. What you can do is to make a container from the view through, e.g. as_vector, as_list. It is *not* true that all sequences return const refs to its elements. Containers definitely return references, otherwise get<N>(c)= expr will not be possible. (That being said, I'm considering allowing mutable views) Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

push_back is designed to be non-mutating. That is why. This is true of all the views returned from 'algos', by design. What you can do is to make a container from the view through, e.g. as_vector, as_list. It is *not* true that all sequences return const refs to its elements. Containers definitely return references, otherwise get<N>(c)= expr will not be possible.
Also, front and back return non-const reference, but thats not the point. During iteration, they return const reference. Thats why this is not possible: struct mutable_func { template<class F> struct result; template<class F, class T, class U> struct result<F(T, U)> { typedef T type; }; template<class T, class U> T operator()(T && x, U y) const { return x+=y; } }; int i = 0; invoke(foo, tuple<int&, int>(i, 1)); //doesn't compile here is the output: deref.hpp:70:16: error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int' return deref_meta::call(i); ^~~~~~~~~~~~~~~~~~~ I believe this is because the iterators are returning a const reference to the element. Is there a workaround for this? Thanks, Paul Fultz II

On 4/3/2012 2:45 AM, paul Fultz wrote:
push_back is designed to be non-mutating. That is why. This is true of all the views returned from 'algos', by design. What you can do is to make a container from the view through, e.g. as_vector, as_list. It is *not* true that all sequences return const refs to its elements. Containers definitely return references, otherwise get<N>(c)= expr will not be possible.
Also, front and back return non-const reference, but thats not the point. During iteration, they return const reference. Thats why this is not possible:
struct mutable_func { template<class F> struct result;
template<class F, class T, class U> struct result<F(T, U)> { typedef T type; };
template<class T, class U> T operator()(T && x, U y) const { return x+=y; } };
int i = 0; invoke(foo, tuple<int&, int>(i, 1)); //doesn't compile
here is the output:
What is 'foo'? Anyway, this (below) compiles fine with me with g++ 4.6.1 and MSVC 10: #include <boost/fusion/functional/invocation/invoke.hpp> #include <boost/fusion/tuple.hpp> #include <boost/fusion/sequence/io.hpp> #include <iostream> struct mutable_func { template<class F> struct result; template<class F, class T, class U> struct result<F(T, U)> { typedef T type; }; template<class T, class U> T operator()(T && x, U y) const { return x+=y; } }; int main() { using namespace boost::fusion; int i = 999; std::cout << invoke(mutable_func(), tuple<int&, int>(i, 1)) << std::endl; return 0; } Prints 1000. What am I missing? Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

----- Original Message -----
From: Joel de Guzman <joel@boost-consulting.com> To: boost-users@lists.boost.org Cc: Sent: Monday, April 2, 2012 11:26 PM Subject: Re: [Boost-users] [fusion] iterating over a sequence with non-const references
On 4/3/2012 2:45 AM, paul Fultz wrote:
push_back is designed to be non-mutating. That is why. This is true of all the views returned from 'algos', by design. What you can do is to make a container from the view through, e.g. as_vector, as_list. It is *not* true that all sequences return const refs to its elements. Containers definitely return references, otherwise get<N>(c)= expr will not be possible.
Also, front and back return non-const reference, but thats not the point. During iteration, they return const reference. Thats why this is not possible:
struct mutable_func { template<class F> struct result;
template<class F, class T, class U> struct result<F(T, U)> { typedef T type; };
template<class T, class U> T operator()(T && x, U y) const { return x+=y; } };
int i = 0; invoke(foo, tuple<int&, int>(i, 1)); //doesn't compile
here is the output:
What is 'foo'? mutable_func foo = {}; I forgot that line.
Anyway, this (below) compiles fine with me with g++ 4.6.1 and MSVC 10:
Yea, sorry about that. It seems it was still calling my own invoke and there was a mistake in that. If you call invoke like this: std::cout << invoke(mutable_func(), forward_as_tuple(i, 1)) << std::endl; There are two errors: invoke.hpp:209:24: error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int' return f( BOOST_PP_ENUM(N,M,~) ); ^~~~~~~~~~~~~~~~~~~~~~~~~ Which I assume is because invoke takes the sequence as a const-reference because it is a temporary. But then why does the other example work(its also a temporary)? The second error is: at_impl.hpp:45:28: error: rvalue reference to type 'int' cannot bind to lvalue of type 'int' return std::get<N::value>(seq); ^~~~~~~~~~~~~~~~~~~~~~~ Which I believe is because of the poor implemetation of tuples in gcc 4.6. I am compiling using clang also. But I do get the same errors in gcc as well. Thanks, Paul Fultz II

On 4/3/2012 10:50 PM, paul Fultz wrote:
Yea, sorry about that. It seems it was still calling my own invoke and there was a mistake in that. If you call invoke like this:
std::cout << invoke(mutable_func(), forward_as_tuple(i, 1)) << std::endl;
There are two errors:
invoke.hpp:209:24: error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int' return f( BOOST_PP_ENUM(N,M,~) ); ^~~~~~~~~~~~~~~~~~~~~~~~~
Which I assume is because invoke takes the sequence as a const-reference because it is a temporary. But then why does the other example work(its also a temporary)?
The int& held in the tuple will always be mutable regardless if the holder tuple is const, that is why it works. I don't know what else you are doing with forward_as_tuple. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

The int& held in the tuple will always be mutable regardless if the holder tuple is const, that is why it works. I don't know what else you are doing with forward_as_tuple.
But I thought since this overload is provided for get: template< std::size_t I, class... Types > typename std::tuple_element<I, tuple<Types...> >::type const& get( const tuple<Types...>& t ); Then it would add const to the reference. Also, forward_as_tuple is just the std::forward_as_tuple. Thanks, Paul Fultz II

On 4/4/2012 11:42 AM, paul Fultz wrote:
The int& held in the tuple will always be mutable regardless if the holder tuple is const, that is why it works. I don't know what else you are doing with forward_as_tuple.
But I thought since this overload is provided for get:
template< std::size_t I, class... Types >
typename std::tuple_element<I, tuple<Types...> >::type const&
get( const tuple<Types...>& t );
Then it would add const to the reference. Also, forward_as_tuple is just the std::forward_as_tuple.
Nope. That is not correct. See: #include <boost/fusion/tuple.hpp> #include <iostream> template <typename T> void foo(T const& t) { using namespace boost::fusion; get<0>(t) = 123; } int main() { using namespace boost::fusion; int i = 999; tuple<int&, int> t(i, 1); foo(t); std::cout << get<0>(t) << std::endl; } The printout is 123. get<1>(t) OTOH is another story. Go figure... Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com
participants (3)
-
Dave Abrahams
-
Joel de Guzman
-
paul Fultz