Search code examples
c++boostsegmentation-faultboost-multi-array

boost multiarray segmentation fault


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).


Solution

  • 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.