c++flann

nanoflann kdtree adaptor raises segfault


I am trying to use nanoflann's kdtree structure in a class of mine. I have the following header for a kdtree adaptor, which is only a small (possibly bad) variation of this, supposed to allow a kd-tree structure with 3d vectors as elements:

// "KdVec3dAdaptor.h"
#include "nanoflann.hpp"
#include <vector>


using Distance = nanoflann::metric_L2;  // Was in template before, dont need it, this way its easier to read
using VectorOfVectorsType = std::vector<std::array<double,3>>;

template <class VectorOfVectorsType>
struct KdVec3dAdaptor
{
    typedef KdVec3dAdaptor<VectorOfVectorsType> self_t;
    typedef typename Distance::template traits<double,self_t>::distance_t metric_t;
    typedef nanoflann::KDTreeSingleIndexAdaptor< metric_t,self_t,3,size_t>  index_t;

    VectorOfVectorsType m_data; 
    index_t* index; //! The kd-tree index for the user to call its methods as usual with any other FLANN index.

    KdVec3dAdaptor() = default;

    KdVec3dAdaptor(const VectorOfVectorsType &mat, const int leaf_max_size = 10) : m_data(mat)
    {
        const size_t dims = mat[0].size();
        index = new index_t( dims, *this /* adaptor */, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size ) );
        index->buildIndex();
    }

    ~KdVec3dAdaptor()
    { // destructor
        delete index;
    }

    KdVec3dAdaptor& operator=(KdVec3dAdaptor&& other)
    {  // move assignment operator
        if (this != &other)
        {  // check that no self-assignment is performed
            delete index;
            index = other.index;
            m_data = other.m_data;
            other.index = nullptr;
        }
        return *this;
    }

    const self_t & derived() const
    {
        return *this;
    }
    self_t & derived()
    {
        return *this;
    }

    inline size_t kdtree_get_point_count() const
    {// Must return the number of data points
        return m_data.size();
    }

    inline double kdtree_get_pt(const size_t idx, int dim) const
    { // Returns the dim'th component of the idx'th point in the class
        return m_data[idx][dim];
    }

    template <class BBOX>
    bool kdtree_get_bbox(BBOX & /*bb*/) const
    { // return false to default to a standard bbox computation loop.
        return false;
    }
};

The problem I am facing is using this kdtree structure as a member variable of a class. A minimal version of that class with the needed type definitions would be:

// "test.cpp"
#include <iostream>
#include <array>
#include "KdVec3dAdaptor.h"

const int dimens = 3;
using vec3d = std::array<double, dimens>;
using vec_arr =  std::vector<vec3d>;
using my_kd_tree =  KdVec3dAdaptor<vec_arr>;

class foo
{
public:
    const int n;
    const double level;
    my_kd_tree init_ps_tree;

    foo(const int &N, const double &level, vec_arr &kset)
    :n(N), level(level)
    {
        init_ps_tree = my_kd_tree(kset,10);
    }
};

The tree is declared in a different method of the class (and I use another class variable of type vec_arr that makes up the trees points). Now here is where I got lost in tracing the error. When I try to call the following method:

void test()
{
    double level = -0.3;
    std::vector<double> arr1 {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
    std::vector<double> arr2 {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
    vec3d vec1 {{1.0, 0.0, 0.0}};
    vec3d vec2 {{1.0, 0.0, 0.0}};
    vec3d vec3 {{1.0, 0.0, 0.0}};
    vec3d vec4 {{1.0, 0.0, 0.0}};
    vec3d vec5 {{1.0, 0.0, 0.0}};
    vec3d vec6 {{1.0, 0.0, 0.0}};
    vec3d vec7 {{1.0, 0.0, 0.0}};
    vec3d vec8 {{1.0, 0.0, 0.0}};
    vec_arr vecarr1 {vec1, vec2, vec3, vec4, vec5, vec6, vec7, vec8};

    int N = 5;
    vec_arr  init_points;

    foo entity(N, level, kset);
    std::cout << entity.n << std::endl;
}

I get a segmentation error, that I figured must be due to the header for the kdtree structure. However, I fail to further trace it, because:

What is it that I'm overlooking? Is there some problem with the destructor in KdVec3dAdaptor? I'm sure it must be something dumb. Thanks in advance for any help and suggestions.


Solution

  • You are deleting object which was not created. Look at destructor of KdVec3dAdaptor class. In test function you created foo object

    foo entity(N, level);
    

    in this constructor my_kd_tree init_ps_tree; member is created by using default constructor of KdVec3dAdaptor which didn't set index member. When test function ends, foo destructor is called and init_ps_tree is destroyed, but index was not set, and

    delete index;
    

    in KdVec3dAdaptor destructor crashes your program. In default ctor of KdVec3dAdaptor you should set index member to 0.

    EDIT

    I don't know why you leave definition of default constructor as

    KdVec3dAdaptor() = default;
    

    look at definition of foo constructor, before entering into body of foo ctor

    {
        init_ps_tree = my_kd_tree(kset,10);   // [1] init_ps_tree was already created
    }
    

    object init_ps_tree was created by using default constructor (index member may contain junk data - random value, if this object is created as local variable). In this line [1] move assignment operator is invoked and below line will be executed

    delete index;  // very dangerous
    

    you can call delete on null pointer, but in your case we don't know what value index holds. First, write ctor of KdVec3dAdaptor as

    KdVec3dAdaptor::KdVec3dAdaptor() : index(0) {}
    

    and next thing, in your move assignment operator you should use std::move to move vector data, now copy of vector is made.

        if (this != &other)
        {  // check that no self-assignment is performed
            delete index;
            index = other.index;
            m_data = std::move(other.m_data); // [2] this vector can be moved
            other.index = nullptr;
        }