Home > Net >  Segmentation fault when inserting struct into a map
Segmentation fault when inserting struct into a map

Time:11-03

The mapBase parameter being populated will represent a coordinate system. I use std::map to easily order coordinates based on x and then pair to a y coordinate that is ordered by an inner map. The value of the inner map will hold extra information later. For now it is populated with dummy data.

I'm attempting to insert a struct into a std::map, but get a segfault on the insertion. The segfault is not always consistent. Sometimes the insertion will work a number of times, and then segfault after no particular set of times.

I have tried adding debug statements that tell me when something was successfully inserted by using the result of the std::map::insert function and looking at the second field of the result. This was only helpful when a segfault did not occur and was generally always true since I clear the mapBase at the start of the called function. I have also tried using smart shared pointers as the final value type of the baseMap instead of just the struct object itself. This did not change my outcome. I also tried allocating *(new cell()) with the same result. I have provided the basic code below.

main:

    #include <map>
    #include <cmath>
    #define DEG_TO_RAD(deg) deg * M_PI / 180

    int main(int argc, char **argv)
    {
      // vector of lidar range, angle pairs
      std::vector<std::pair<double, double>> lidarData {{0.585, -179.41},
                                                        {0.689, -151.672},
                                                        {0.671, 56.6557},
                                                        {0.717, 122.164},
                                                        {0.611, 159.344},
                                                        {0.586, 175.279}};
    
      // convert returns to x, y coordinate point
      std::vector<Eigen::Matrix<double, 2, 1>> points;
      for(const auto& beam : lidarData)
      {
        double angle = DEG_TO_RAD(beam.second);
        double range = beam.first;
        double x = range * cos(angle); // r * cos(theta)
        double y = range * sin(angle); // r * sin(theta)
        Eigen::Matrix<double, 2, 1> point;
        point << x, y;
        points.emplace_back(point);
      }
    
      auto* newA = new A();
      newA->doSomething(points);
      
      return 0;
    }

Header:

class A {
    public:
        A();

        ~A();
  
        void doSomething(std::vector<Eigen::Matrix<double, 2, 1>> &points);
  
    private:
        struct cell {
            Eigen::Matrix<double, 2, 1> mean;
            Eigen::Matrix<double, 2, 2> covariance;
            std::vector<double> x_m {};
            std::vector<double> y_m {};
            std::vector<Eigen::Matrix<double, 2, 1>> hits {};

            cell();
        };

        // define a map keyed by a x coordinate with a value of  std::map.
        // inner map is keyed by a y coordinate with a value of struct type cell.
        typedef std::map<double, std::map<double, cell>> map;
        map mapBase;
    }
}

Source

A::A() {}

A::~A() {}

void A::doSomething(std::vector<Eigen::Matrix<double, 2, 1>> &points) {
    mapBase.clear();
    for (const auto &point : points) {
        auto x = point.x();
        auto y = point.y();

        auto xIt = mapBase.find(x);
        if (xIt == mapBase.end()) { // coordinate does not exist if true
            std::map<double , cell> yPair;
            yPair.insert(std::make_pair(y, cell()));  // Segfault happens here
            mapBase.insert(std::make_pair(x, yPair));
        } else { // x exist in map, but check if y does
            auto yIt = mapBase.at(x).find(y);
            if (yIt == mapBase.at(x).end()) { // y does not exist at x if true
                mapBase.at(x).insert(std::make_pair(y, cell()));
            }
        }

        // Emplace values at the new cell in the map. 
        mapBase.at(x).at(y).x_m.emplace_back(x);
        mapBase.at(x).at(y).y_m.emplace_back(y);
        mapBase.at(x).at(y).hits.emplace_back(Eigen::Matrix<double, 2, 1>());
        mapBase.at(x).at(y).mean.setOnes();
        mapBase.at(x).at(y).covariance.setOnes();
    }
};

A::cell::cell() {
    mean.setZero();
    covariance.setOnes();
    x_m.clear();
    y_m.clear();
    hits.clear();
}

On regular execution of the code I just get a Segmentation fault (core dumped) at the insertion of the struct. Using gdb, the back trace is as follows:

#0  std::pair<double const, cell>::pair<double, A::cell, true> (__p=..., this=<optimized out>) at /usr/include/c  /7/bits/stl_pair.h:362
#1  __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<double const, A::cell> > >::construct<std::pair<double const, A::cell>, std::pair<double, A::cell> > (this=<optimized out>, __p=<optimized out>) at /usr/include/c  /7/ext/new_allocator.h:136
#2  std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<double const, A::cell> > > >::construct<std::pair<double const, A::cell>, std::pair<double, A::cell> > (__a=..., __p=<optimized out>) at /usr/include/c  /7/bits/alloc_traits.h:475
#3  std::_Rb_tree<double, std::pair<double const, A::cell>, std::_Select1st<std::pair<double const, A::cell> >, std::less<double>, std::allocator<std::pair<double const, A::cell> > >::_M_construct_node<std::pair<double, A::cell> > (this=0x7fffffffd6d0, __node=0x55555585ed90) at /usr/include/c  /7/bits/stl_tree.h:626
#4  std::_Rb_tree<double, std::pair<double const, A::cell>, std::_Select1st<std::pair<double const, A::cell> >, std::less<double>, std::allocator<std::pair<double const, A::cell> > > >::_M_create_node<std::pair<double, A::cell> > (this=0x7fffffffd6d0) at /usr/include/c  /7/bits/stl_tree.h:643
#5  std::_Rb_tree<double, std::pair<double const, A::cell>, std::_Select1st<std::pair<double const, A::cell> > > std::less<double>, std::allocator<std::pair<double const, A::cell> > >::_M_emplace_unique<std::pair<double, A::cell> > (this=this@entry=0x7fffffffd6d0) at /usr/include/c  /7/bits/stl_tree.h:2351
#6  0x0000555555596ddd in std::map<double, A::cell, std::less<double>, std::allocator<std::pair<double const, A::cell> > >::emplace<std::pair<double, A::cell> > (this=0x7fffffffd6d0) at /usr/include/c  /7/bits/stl_map.h:569
#7  A::doSomething (this=this@entry=0x5555558082d0, points=std::vector with 49 elements = {...}) at ...

The backtrace did not make obvious the issue, but further debugging has shown that removing the mean and covariance from the struct allows the application to run without faulting. I could just separate them into separate parameters, but that is not really the correct solution in my opinion. Maybe in the copy invocation when making a pair causes the issue and handling of Eigen parameters are mismanaged? Any help in the understanding and solution to my issue will be greatly appreciated.

CodePudding user response:

You had some mistakes in your above given code snippet which i have removed. The program works now without giving segmentation fault. You could try this and confirm me of the same.

mainneweighen.cpp

#include <vector>
#include <Eigen/Dense>
#include "neweigen.h"
int main(int argc, char **argv)
    {
      std::vector<std::pair<double, double>> lidarData {{0.585, -179.41},
                                                        {0.689, -151.672},
                                                        {0.671, 56.6557},
                                                        {0.717, 122.164},
                                                        {0.611, 159.344},
                                                        {0.586, 175.279}};
    
    
      std::vector<Eigen::Matrix<double, 2, 1>> points;
      for(const auto& beam : lidarData)
      {
        double x = beam.first * cos(beam.second); // r * cos(theta)
        double y = beam.first * sin(beam.second); // r * sin(theta)
        Eigen::Matrix<double, 2, 1> point;
        point << x, y;
        points.emplace_back(point);
      }
    
      auto* newA = new A();
      newA->doSomething(points);
      
      return 0;
    }

neweigen.cpp

#include "neweigen.h"

A::A() {}

A::~A() {}

void A::doSomething(std::vector<Eigen::Matrix<double, 2, 1>> &points) {
    mapBase.clear();
    for (const auto &point : points) {
        auto x = point.x();
        auto y = point.y();

        auto xIt = mapBase.find(x);
        if (xIt == mapBase.end()) { // coordinate does not exist if true
            std::map<double , cell> yPair;
            yPair.insert(std::make_pair(y, cell()));  // Segfault happens here
            mapBase.insert(std::make_pair(x, yPair));
        } else { // x exist in map, but check if y does
            auto yIt = mapBase.at(x).find(y);
            if (yIt == mapBase.at(x).end()) { // y does not exist at x if true
                mapBase.at(x).insert(std::make_pair(y, cell()));
            }
        }

        // Emplace values at the new cell in the map. 
        mapBase.at(x).at(y).x_m.emplace_back(x);
        mapBase.at(x).at(y).y_m.emplace_back(y);
        mapBase.at(x).at(y).hits.emplace_back(Eigen::Matrix<double, 2, 1>());
    }
}

A::cell::cell() {
    mean.setZero();
    covariance.setOnes();
    x_m.clear();
    y_m.clear();
    hits.clear();
}


neweigen.h

#ifndef FILE_H
#define FILE_H
#include <vector>
#include <Eigen/Dense>
#include <map>
class A {
    public:
        A();

        ~A();
  
        void doSomething(std::vector<Eigen::Matrix<double, 2, 1>> &points);
  
    private:
        struct cell {
            Eigen::Matrix<double, 2, 1> mean;
            Eigen::Matrix<double, 2, 2> covariance;
            std::vector<double> x_m {};
            std::vector<double> y_m {};
            std::vector<Eigen::Matrix<double, 2, 1>> hits {};

            cell();
        };

        // define a map keyed by a x coordinate with a value of  std::map.
        // inner map is keyed by a y coordinate with a value of struct type cell.
        typedef std::map<double, std::map<double, cell>> map;
        map mapBase;
    };


#endif

The above three files work without giving any segmentation fault.

CodePudding user response:

I think you have to define first the types of the pair: std::pair<double,cell> new_pair = makepair(y,cell) and then insert it into the map.

  • Related