开发者

Boost MultiArray Copy Constructor

I have got a problem understanding the copy constructor implemen开发者_高级运维tation of boost::multi_:array.

When I try the following

std::vector<double> a;
std::vector<double> b;
a.resize(12);
b.resize(10);
a=b;

everything works out fine,

but when I try

boost::multi_array<double,1> a;
boost::multi_array<double,1> b;
a.resize(boost::extents[12]);
b.resize(boost::extents[10]);
a=b;

I get a crash.

I expected the same behaviour, but I also could not find anything useful in the documentation.

Does anyone have an idea ?

Regards

awallrab


It looks like boost::multi_array works just like std::valarray regarding assignment, that is the size of the 2 arrays must match.

According to the documentation:

Each of the array types multi_array, multi_array_ref, subarray, and array_view can be assigned from any of the others, so long as their shapes match.


The semantics of the boost::multi_array assignment operators are infuriatingly bad, wasting countless man-hours of developer time searching for answers like this one, and worse yet introducing lord knows how many subtle bugs that have gone undetected in projects all over the world, for reasons that I could best describe as laughable arrogance.

You can fix the assigment operators inside multi_array.hpp thusly:

  // replacement for arrogant boost::multi_array assignment semantics
  template <typename ConstMultiArray>
  multi_array& operator=(const ConstMultiArray& other) {
    // deallocate
    deallocate_space();
    base_ = 0;
    allocated_elements_ = 0;
    // copy members of const_multi_array_ref
    storage_ = other.storage_;
    extent_list_ = other.extent_list_;
    stride_list_ = other.stride_list_;
    index_base_list_ = other.index_base_list_;
    origin_offset_ = other.origin_offset_;
    directional_offset_ = other.directional_offset_;
    num_elements_ = other.num_elements_;
    // allocate
    allocator_ = other.allocator_;
    allocate_space();
    // iterator-based copy
    std::copy(other.begin(),other.end(),this->begin());
    return *this;
  }

  multi_array& operator=(const multi_array& other) {
    if (&other != this) {
      // deallocate
      deallocate_space();
      base_ = 0;
      allocated_elements_ = 0;
      // copy members of const_multi_array_ref
      storage_ = other.storage_;
      extent_list_ = other.extent_list_;
      stride_list_ = other.stride_list_;
      index_base_list_ = other.index_base_list_;
      origin_offset_ = other.origin_offset_;
      directional_offset_ = other.directional_offset_;
      num_elements_ = other.num_elements_;
      // allocate
      allocator_ = other.allocator_;
      allocate_space();
      // copy
      boost::detail::multi_array::copy_n(other.base_,other.num_elements(),base_);
    }
    return *this;
  }

Contrary to the irrelevant beliefs of the boost authors in this case, this sort of fix will not break anything. The reason it won't break anything is that the behavior of the original library is to straight up crash with an assertion if sizes don't match, whereas the new code will resize the multi_array first and then not crash. So unless you have test cases that require the application to crash, no test cases can possibly be broken by this change.

Saving countless hours of productivity and protecting against countless undetected bugs absolutely trumps any semantic ideologies the authors might have. There. That's my rant. You're welcome.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜