I`m writing a code for which I'm using a 3 dimensional boost multiarray to save coordinates. But I always get a segmentation fault at some point. How are boost multiarray sizes limited and how can I get around those limits?
Here is a simplified test code that reproduces the problem:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <string>
#include <algorithm>
#include <map>
#include <boost/multi_array.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "Line.h"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
typedef struct {
Eigen::Vector3d coords;
int gpHostZone;
int gpHostFace;
int calculated;
} Vertex;
class LGR {
public:
LGR (int i, int j, int k) :
grid(boost::extents[i][j][k])
{
};
std::string name;
std::vector<int> hostZones;
std::vector<int> refine;
boost::multi_array<Vertex*, 3> grid;
std::vector<double> data;
};
int main(void){
LGR lgr(11,11,21);
std::cout << lgr.grid.size();
std::vector<LGR> v;
std::vector<Vertex> vertexDB;
for(int i = 0; i < 1; i++ ){
for(int j = 0; j < lgr.grid.size(); j++ ){
for(int k = 0; k < lgr.grid[0].size(); k++ ){
for(int l = 0; l < lgr.grid[0][0].size(); l++ ){
Vertex coord;
coord.coords << i,j,k;
coord.gpHostZone = 0;
coord.gpHostFace = 0;
coord.calculated = 0;
vertexDB.push_back(coord);
lgr.grid[j][k][l] = &(vertexDB.back());
}
}
}
for(int j = 0; j < lgr.grid.size(); j++ ){
for(int k = 0; k < lgr.grid[0].size(); k++ ){
for(int l = 0; l < lgr.grid[0][0].size(); l++ ){
std::cout << "At ("<< i << ","<< j << ","<< k << "," << l << ")\n";
std::cout << lgr.grid[j][k][l]->coords<<"\n\n";
}
}
}
}
return 1;
}
Please do not comment on the includes. I just copy and pasted from the actual code. Most of the are probably not needed here. The dimensions are from a real example, so I actually need those kind of dimensions (and probably more).
The following is a definite issue that leads to undefined behavior, and doesn't have anything to do with boost::multiarray
.
These lines:
std::vector<Vertex> vertexDB;
//...
vertexDB.push_back(coord);
lgr.grid[j][k][l] = &(vertexDB.back());
resizes the vertexDB
vector and then stores a pointer to the last item in the vector to lgr.grid[j][k][l]
. The problem with this is that pointers and iterators to items in a vector may become invalidated due to the vector having to reallocate memory when resizing the vector.
This manifests itself later here, in the subsequent loop:
std::cout << lgr.grid[j][k][l]->coords<<"\n\n";
There is no guarantee that the address you assigned previously is valid.
A quick fix for this is to use a std::list<Vertex>
, since adding items to a std::list
does not invalidate iterators / pointers.