I have this apparently simple code, but boost complains about a dimension mismatch:
#include <iostream>
#include <fstream>
#include "boost/multi_array.hpp"
struct t_struct {
boost::multi_array<double, 2> grid_mat;
};
t_struct get_big_data(int dim_1, int dim_2) {
boost::multi_array<double,2> grid_mat(boost::extents[dim_1][dim_2]);
for (int j=0;j<dim_2;++j){
for (int i=0; i<dim_1;++i){
grid_mat[i][j] = i + j;
}
}
t_struct all_data;
all_data.grid_mat.resize(boost::extents[dim_1][dim_2]);
all_data.grid_mat = grid_mat;
return all_data;
}
int main(int argc, char *argv[]) {
t_struct all_data_2;
int dim_1 = 320;
int dim_2 = 6;
all_data_2 = get_big_data(dim_1,dim_2);
return 0;
}
The runtime error I am receiving is:
/usr/include/boost/multi_array/multi_array_ref.hpp:483: boost::multi_array_ref<T, NumDims>& boost::multi_array_ref<T, NumDims>::operator=(const ConstMultiArray&) [with ConstMultiArray = boost::multi_array<double, 2>; T = double; long unsigned int NumDims = 2]: Assertion `std::equal(other.shape(),other.shape()+this->num_dimensions(), this->shape())' failed.
I can't figure out what I am doing wrong.
The code compiles and I was expecting it would simple to populate a boost::multi_array within a struct.
You're copying the matrix several times. The first copy is fine:
all_data.grid_mat = grid_mat;
That is because you were aware that multi_array
assignment operator requires the destination array to have the same shape, and you sized it accordingly.
However, the second copy happens in main
:
t_struct all_data_2;
all_data_2 = get_big_data(320, 6);
Here, all_data_2
already was default constructed and hence does NOT have the required shape. You can simply elide the 2-phase init by using copy-initialization:
t_struct all_data_2 = get_big_data(320, 6);
This means that the compiler (in this case) can elide the assignment, instead using the auto-generated copy constructor. You can generalize this by "simply" returning an aggregate-constructed value as well:
#include "boost/multi_array.hpp"
#include <iostream>
struct t_struct {
boost::multi_array<double, 2> grid_mat;
};
t_struct get_big_data(int n, int m) {
boost::multi_array<double, 2> grid_mat(boost::extents[n][m]);
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
grid_mat[i][j] = i + j;
return {grid_mat};
}
int main() {
t_struct all_data_2 = get_big_data(320, 6);
}
Doing away with the surprises. Because they are just inviting bugs! You can specify the semantics you want in t_struct
by defining your own set of special member functions:
Live On Coliru
#undef NDEBUG
#include "boost/multi_array.hpp"
#include <iostream>
using GridMat = boost::multi_array<double, 2>;
using Shape = std::array<size_t, 2>;
struct t_struct {
t_struct() = default;
explicit t_struct(GridMat mat) : grid_mat_(std::move(mat)) {}
t_struct(t_struct const& rhs) : grid_mat_(rhs.grid_mat_) {}
Shape shape() const {
auto shape = grid_mat_.shape();
return {shape[0], shape[1]};
}
t_struct& operator=(t_struct const& rhs) {
grid_mat_.resize(rhs.shape());
grid_mat_ = rhs.grid_mat_;
return *this;
}
private:
GridMat grid_mat_;
};
t_struct get_big_data(int n, int m) {
GridMat grid_mat(boost::extents[n][m]);
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
grid_mat[i][j] = i + j;
return t_struct(grid_mat);
}
#include <fmt/ranges.h>
int main() {
t_struct clone;
fmt::print("default-constructed shape: {}\n", clone.shape());
clone = get_big_data(320, 6);
fmt::print("assigned shape: {}\n", clone.shape());
clone = get_big_data(6, 78);
fmt::print("re-assigned shape: {}\n", clone.shape());
}
Printing
default-constructed shape: [0, 0]
assigned shape: [320, 6]
re-assigned shape: [6, 78]