Reputation: 1643
I want to have class, which uses boost::geometry::index::rtree
for spatial indexers. This class alone should know about boost, so I use something like this:
struct VeryImportantInfo
{
...
float x;
float y;
}
class Catalogue
{
...
public:
std::vector<std::shared_ptr<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);
protected:
using point = bg::model::point<float, 2, bg::cs::cartesian>;
using value = std::pair<point, std::shared_ptr<VeryImportantInfo> >;
using box = bg::model::box<point>;
boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
}
std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
???
}
I don't know how to do query properly (Please don't look at this awful vector return by copy, it is just for example sake). I can do this:
std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
std::vector<value> result_s;
rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));
std::vector<std::shared_ptr<VeryImportantInfo> > results;
results.reserve(result_s.size());
for( auto& p : result_s)
{
results.emplace_back(p.second);
}
return results;
}
I want to know, how can I get rid of internal copy(not return copy, results.emplace_back(p.second);
- this one). Because I can have more that 10k results in result_s
and it will be a waste.
Thank you!
Upvotes: 4
Views: 392
Reputation: 392893
Update to the comment
If the worry was about the temporary vector in the first place, just don't use one. You can use the qbegin()
/qend()
free functions from boost::geometry::index
:
std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
auto b = bgi::qbegin(rtree, bgi::intersects(query_box)),
e = bgi::qend(rtree);
auto range = boost::make_iterator_range(b, e);
using namespace boost::adaptors;
return boost::copy_range<std::vector<std::shared_ptr<VeryImportantInfo>>>(
range | transformed([](value const& p) { return p.second; }));
}
In fact, if the rtree is known to be constant, you could even return the lazy range directly and not allocate even the single vector.
original/old answer text follows:
You cannot copy shared pointers without reference counting.
Of course, you could change the value
pair to contain a reference to the shared_ptr, but instead you could then employ either raw references (std::reference_wrapper) or weak_ptr
.
std::reference_wrapper<T>
Here's my take on it with raw references (just keep your important data around :)):
#include <iostream>
#include <vector>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/index/rtree.hpp>
namespace bg = boost::geometry;
namespace bgi = bg::index;
struct VeryImportantInfo {
float x;
float y;
};
VeryImportantInfo a = { 2, 2 };
VeryImportantInfo b = { 3, 3 };
VeryImportantInfo c = { 4, 4 };
class Catalogue
{
public:
Catalogue() {
rtree.insert(value(point(a.x, a.y), a));
rtree.insert(value(point(b.x, b.y), b));
rtree.insert(value(point(c.x, c.y), c));
}
std::vector<std::reference_wrapper<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);
protected:
using point = bg::model::point<float, 2, bg::cs::cartesian>;
using value = std::pair<point, std::reference_wrapper<VeryImportantInfo> >;
using box = bg::model::box<point>;
boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
};
std::vector<std::reference_wrapper<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
std::vector<value> result_s;
rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));
std::vector<std::reference_wrapper<VeryImportantInfo> > results;
results.reserve(result_s.size());
for(auto& p : result_s) {
results.push_back(p.second);
}
return results;
}
int main() {
Catalogue cat;
for (VeryImportantInfo& vii : cat.FindIn(1,2,3,4))
std::cout << vii.x << ", " << vii.y << "\n";
}
std::weak_ptr<T>
Here is the same with weak_ptr<>
. One could argue this doesn't solve much (because ref counting is still happening) but at least less work is required.
#include <iostream>
#include <memory>
#include <vector>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/index/rtree.hpp>
namespace bg = boost::geometry;
namespace bgi = bg::index;
struct VeryImportantInfo {
float x;
float y;
};
auto a = std::make_shared<VeryImportantInfo>(VeryImportantInfo{2, 2});
auto b = std::make_shared<VeryImportantInfo>(VeryImportantInfo{3, 3});
auto c = std::make_shared<VeryImportantInfo>(VeryImportantInfo{4, 4});
class Catalogue
{
public:
Catalogue() {
rtree.insert(value(point(a->x, a->y), a));
rtree.insert(value(point(b->x, b->y), b));
rtree.insert(value(point(c->x, c->y), c));
}
std::vector<std::weak_ptr<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);
protected:
using point = bg::model::point<float, 2, bg::cs::cartesian>;
using value = std::pair<point, std::shared_ptr<VeryImportantInfo> >;
using box = bg::model::box<point>;
boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
};
std::vector<std::weak_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
std::vector<value> result_s;
rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));
std::vector<std::weak_ptr<VeryImportantInfo> > results;
results.reserve(result_s.size());
for(auto& p : result_s) {
results.push_back(p.second);
}
return results;
}
int main() {
Catalogue cat;
for (auto& vii_p : cat.FindIn(1,2,3,4))
if (auto vii = vii_p.lock())
std::cout << vii->x << ", " << vii->y << "\n";
}
Upvotes: 2