Recently, Hanan Sadar asked about a multi_array fill implementation [1]. The standard std::fill_n(x.data(),x.num_elements(),v) answer only works when x is a boost::multi_array or boost::multi_array_ref but does not handle the general MultiArray concept.
I decided to implement an arbitrary dimensional MultiArray fill capable of handling such views correctly. It's been absolutely miserable for reasons I don't understand but which resemble comments from James Amundson's recent thread [2]. Attached is my attempt with two exasperating lines marked by "Cannot use reference, why?".
For a non-multi_array, non-multi_array_ref instance (i.e. a view) using a signature like template< class Array, class V > void operator()(Array x, const V &v); allows the code to compile and operate correctly. However, it incurs pass-by-value overhead. Using a signature like template< class Array, class V > void operator()(Array &x, const V &v); using pass-by-reference dies at compilation time. All of my attempts to use MultiArray's associated typedefs die, including hours of mucking with MultiArray::reference.
What am I missing here? Why can't I pass a general MultiArray view type by reference?
The problem might be the body of fill_functor for the general case. "*i" will produce a temporary (a subarray), which you are then trying to pass immediately by reference to the smaller fill_functor case. C+ + doesn't let you pass temporaries by reference, which is a real problem when you need proxy references like MultiArray does. The solution should be as follows:
for (typename Array::iterator i = x.begin(); i != x.end(); + +i) { typename Array::iterator::reference ri = *i; f(ri,v); }
That is, assign the temporary to a variable before you call f.
> Recently, Hanan Sadar asked about a multi_array fill implementation > [1]. The standard std::fill_n(x.data(),x.num_elements(),v) answer > only works when x is a boost::multi_array or boost::multi_array_ref > but does not handle the general MultiArray concept.
> I decided to implement an arbitrary dimensional MultiArray fill > capable of handling such views correctly. It's been absolutely > miserable for reasons I don't understand but which resemble comments > from James Amundson's recent thread [2]. Attached is my attempt with > two exasperating lines marked by "Cannot use reference, why?".
> For a non-multi_array, non-multi_array_ref instance (i.e. a view) > using a signature like > template< class Array, class V > void operator()(Array x, const V > &v); > allows the code to compile and operate correctly. However, it incurs > pass-by-value overhead. Using > a signature like > template< class Array, class V > void operator()(Array &x, const > V &v); > using pass-by-reference dies at compilation time. All of my attempts > to use MultiArray's associated typedefs die, including hours of > mucking with MultiArray::reference.
> What am I missing here? Why can't I pass a general MultiArray view > type by reference?
> The problem might be the body of fill_functor for the general case. "*i" > will produce a temporary (a subarray), which you are then trying to pass > immediately by reference to the smaller fill_functor case. C++ doesn't let > you pass temporaries by reference, which is a real problem when you need > proxy references like MultiArray does. The solution should be as follows:
> for (typename Array::iterator i = x.begin(); i != x.end(); ++i) { > typename Array::iterator::reference ri = *i; > f(ri,v); > }
Ron's suggestion did fix my problem. Thanks Ron.
Just in case anyone's ever searching for a general MultiArray fill implementation, I've attached the now working code/test case.
This might not be a bad example to add to the MultiArray tutorial, both because it touches on an easy mistake and because people ask about fill with some frequency.
>> The problem might be the body of fill_functor for the general >> case. "*i" >> will produce a temporary (a subarray), which you are then trying to >> pass >> immediately by reference to the smaller fill_functor case. C++ >> doesn't let >> you pass temporaries by reference, which is a real problem when you >> need >> proxy references like MultiArray does. The solution should be as >> follows:
>> for (typename Array::iterator i = x.begin(); i != x.end(); + >> +i) { >> typename Array::iterator::reference ri = *i; >> f(ri,v); >> }
> Ron's suggestion did fix my problem. Thanks Ron.
> Just in case anyone's ever searching for a general MultiArray fill > implementation, I've attached the now working code/test case.
> This might not be a bad example to add to the MultiArray tutorial, > both > because it touches on an easy mistake and because people ask about > fill with some frequency.