How to elegantly iterate through the nodes of a boost R-tree while it's being modified?

104 Views Asked by At

I'm using a boost R-tree (boost version 1.65.1 and C++ 17) to iterate over some 2D points. What I want to do is to perform neighbour searches for each node (so 'detect' neighbouring nodes), do some stuff with the results, and afterwards remove all query result nodes from the r-tree. Then I want to iterate over the remaining nodes and repeat until I reached the end.

The problem now is that modifying the r-tree this way not only may but in my case does invalidate the iterator. This basically results in a never ending for-loop among other problems.

Right now I don't see an other way to solve this than to reset the iterator to begin().

So my question is:

Is there a more elegant way to do this?

The basic code snippet using an r-tree which stores pairs of 2D-points and some number:

for (auto it = someRTree.begin(); it != someRTree.end(); ++it) {
    std::vector<std::pair<point, size_t>> results;
    // Creating a query box around the current node
    bgm::box<point> queryBox(point(bg::get<0>(it->first) - someConst,
                                   bg::get<1>(it->first) - someConst),
                             point(bg::get<0>(it->first) + someConst,
                                   bg::get<1>(it->first) + someConst));
    someRTree.query(bgi::intersects(queryBox), std::back_inserter(results));

    // do something with the results
    // ...

    someRTree.remove(results); // <- This here will invalidate the iterator.
}
1

There are 1 best solutions below

3
Zacryon On

Since the query results are going to be removed from the r-tree anyway, I found a solution, which is a little bit simpler. Basically the for loop is transformed into a while loop and the iterator is set at the beginning of each loop iteration to the beginning of the r-tree. This also makes case handling unnecessary which might otherwise be necessary when using a for loop.

while (!someRTree.empty()) {
    auto it = someRTree.begin(); // Set iterator at beginning of while loop.

    std::vector<std::pair<point, size_t>> results;
    // Creating a query box around the current node
    bgm::box<point> queryBox(point(bg::get<0>(it->first) - someConst,
                                   bg::get<1>(it->first) - someConst),
                             point(bg::get<0>(it->first) + someConst,
                                   bg::get<1>(it->first) + someConst));
    someRTree.query(bgi::intersects(queryBox), std::back_inserter(results));

    // do something with the results
    // ...

    someRTree.remove(results); // <- Iterator will still be invalidated, but
                               // that does not affect us here anymore.
}

However, since I am not really iterating the iterator I could also just use someRTree.begin() in place of the iterator it. This is semantically identical. I just left it this way for convenience reasons.

Still, I wonder if there is a better solution. When removing elements from STL containers it's possible to get a iterator pointing to the element following the removed one. Something similar in boost could be advantageous. But there you can only return the number of elements which were removed.