Boost logo

Boost Users :

From: Dr Johann A. Briffa (j.briffa_at_[hidden])
Date: 2008-07-11 06:17:04


I realize this topic has been discussed before (I've seen at least a
thread in 2006 and another in 2007), but I believe there is more to add.
At issue is the decision that multi_array objects cannot be assigned
[with the operator=() that is] unless the target is already the same
size as the source. Further, there is no documented way to easily resize
an existing array to the same size and range as some other array.

Let me start with the first issue: others have already said before that
making multi_array assignable would be a good thing. Ron disagrees on
this point, and from what I gather his principal reason is that
multi_array should have the same semantics as multi_array_ref etc.
[please do correct me if I'm wrong here, I've only had a day to sort
this out]. I am unsure why multi_array_ref etc cannot also have such an
interface, but then I haven't used that class enough so my knowledge is
very limited there.

As far as my needs are concerned, a multi_array that cannot be assigned
is next to useless. Allow me to elaborate: I am using multi_array as a
replacement to my own (old) 2/3D containers, as recently I've had need
for higher dimensions. I have classes containing array members, where
the size of the arrays is dynamic through the object lifetime. So my
class default constructor uses the array default constructor for its
member objects, creating empty arrays. Eventually as my classes get
used, the array members are updated accordingly, and get to have some
non-zero size. Now the problem is that my classes need to be assignable,
and the only way that can be achieved is if the array members are
assignable; the alternative is to have to write a specific assignment
operator for any of my classes that use arrays, resizing each member of
the target before copying. Clearly that would involve a lot of extra effort.

The second (admittedly minor) problem is that the only way to resize the
target is unclean and the semantics are bound to the dimensionality:

target.resize(boost::extents[source.shape()[0]][source.shape()[1]]);

etc. I would rather have something like:

target.resize(source.extents());

Ron had mentioned in 2007 that the lack of such an interface was an
oversight, but there is still no solution.

I am bringing up these issues again because:

1) I am not convinced there is any valid reason not to have multi_array
assignable. I do not buy the performance issue, as this would only kick
in if (a) assignment is within a performance-sensitive path and (b) the
target is not already of the same size. Further, at the very least there
could be a derived class (somewhat like what I document below) that
allows such assignment.

2) It would be nice to have the clean resize interface as part of the
multi_array class.

As an interim solution for myself, I derived a new class from
multi_array that allows assignment and does the resize automagically. I
am copying the code below for public benefit. Notes:

i) anyone is free to (re)use this code under the same license as
multi_array; in particular, Ron, please do feel free to adapt and reuse
within a future multi_array as you see fit.

ii) while the interface provided is sufficient for the way I use the
class, it certainly needs to be adapted to be completely generic.

Johann


#ifndef __multi_array_h
#define __multi_array_h

#include "config.h"
#include <boost/multi_array.hpp>

namespace boost {

/*!
   \brief Assignable version of Boost MultiArray.
   \author Johann Briffa

   \par Version Control:
   - $Revision: 1488 $
   - $Date: 2008-07-11 10:35:16 +0100 (Fri, 11 Jul 2008) $
   - $Author: jabriffa $
*/

template<typename T, std::size_t NumDims>
class assignable_multi_array : public multi_array<T,NumDims> {
public:
   /*! \name Constructors / Destructors */
   explicit assignable_multi_array() :
      multi_array<T,NumDims>()
      {};
   explicit assignable_multi_array(const detail::multi_array::extent_gen<NumDims>& ranges) :
      multi_array<T,NumDims>(ranges)
      {};
   explicit assignable_multi_array(const assignable_multi_array& x) :
      multi_array<T,NumDims>(dynamic_cast< const multi_array<T,NumDims>& >(x))
      {};
   // @}
   /*! \name Assignment */
   assignable_multi_array& operator=(const assignable_multi_array& x)
      {
      if(!std::equal(this->shape(), this->shape()+this->num_dimensions(), x.shape()))
         {
         resize(x.extents());
         for(std::size_t i=0; i<NumDims; i++)
            libbase::trace << "Output Extent " << i << " = " << this->shape()[i] << "\n";
         }
      dynamic_cast< multi_array<T,NumDims>& >(*this) = dynamic_cast< const multi_array<T,NumDims>& >(x);
      return *this;
      }
   // @}
   /*! \name Informative functions */
   /*! \brief Get array extents description
      Returns an object describing the array extents, in a format suitable
      for use with resize().
   */
   detail::multi_array::extent_gen<NumDims> extents() const
      {
      typedef typename multi_array<T,NumDims>::extent_range extent_range;
      detail::multi_array::extent_gen<NumDims> extents_list;
      for(std::size_t i=0; i<NumDims; i++)
         {
         extents_list.ranges_[i] = extent_range(this->index_bases()[i], this->index_bases()[i]+this->shape()[i]);
         libbase::trace << "Input Extent " << i << " = " << this->shape()[i] << "\n";
         }
      return extents_list;
      }
   // @}
};

}; // end namespace

#endif


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net