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:
level
or n
) from the class fixes the problemnew
to create the instance doesn't change anythingWhat 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.
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;
}