1

I'm trying to create an AABBTree structure where each node knows its parent and children.

Within my AABBTreeNode class, the parent is stored as a std::shared_ptr<AABBTreeNode> and the children as a std::vector<std::weak_ptr<AABBTreeNode>>. This is done to avoid circular references as pointed out in this Stackoverflow post.

To add a new child I first calculate a valid bounding box and call the following function:

void AABBTreeNode::AddChild(std::shared_ptr<open3d::geometry::AxisAlignedBoundingBox> partitionBound)
{
    auto child = std::make_shared<AABBTreeNode>(shared_from_this(), m_Mesh, partitionBound);
    m_Children.push_back(child);
}

After calling this function, I want to use the child - however the lock function always returns an empty std::shared_ptr<T>. Here is the calling code (Creating Children and filling them with triangles should be done recoursively):

void AABBTreeNode::AddTriangles(std::vector<Eigen::Vector3d>& centers)
{
    auto center = m_Bbox->GetCenter();
    for (auto& boundPoint : m_Bbox->GetBoxPoints())
    {
        // here is code, which calculates the minBound and maxBound for 
        // up to 8 Children

        auto adjustedPartitionBound = std::make_shared<open3d::geometry::AxisAlignedBoundingBox>(minBound, maxBound);
        AddChild(adjustedPartitionBound);
        auto child = m_Children.back().lock(); 
        if (child)
        {
            //this part is never reached, since child is alway empty...
            child->AddTriangles(partitionCenters);
        }
    }
}

Why is child always empty in this case? I tried to include only important parts of the code, let me know if some information is missing.

Roland Deschain
  • 2,211
  • 19
  • 50
  • Just noticed: `m_Children.back().lock()` returns an empty pointer as soon as the program leaves the `AddChild()` function. – Roland Deschain Sep 01 '21 at 13:46
  • The only owner of the newly created node is `child` in `AddChild`, and when that owner dies, the node goes away. – molbdnilo Sep 01 '21 at 13:49
  • 4
    I would expect the parent to own its children, not the other way around. – molbdnilo Sep 01 '21 at 13:51
  • @molbdnilo as mentioned in my comment below, do you mean swapping, so that the parent becomes the weak pointer and the children are smart pointers? – Roland Deschain Sep 01 '21 at 13:55
  • Yes, but thinking about these types as pointers is often problematic (I personally don't like the term "smart pointer", because they're neither). `shared_ptr` is "this object can have one or several owners, and goes away when nobody wants it anymore", and `weak_ptr` is "I can become an owner of this, *if* it exists when I want it". If you think about it, "a parent node is destroyed when it has no children" seems a bit odd, but "a child node is destroyed when it has no parent" not quite so much. – molbdnilo Sep 01 '21 at 14:07
  • @molbdnilo Thank you this is a quite helpful explanation! :) – Roland Deschain Sep 01 '21 at 14:09
  • As an alternative, you could have `std::vector children` and `AABBTreeNode * parent`, which is safe, as a child can't outlive it's parent. In that case you will need to define the special members (copied or moved children need to point to the new parent) – Caleth Sep 01 '21 at 14:48

1 Answers1

2
{
    auto child = std::make_shared<AABBTreeNode>(shared_from_this(), m_Mesh, partitionBound);
    m_Children.push_back(child);
}

Here, the automatic variable child is the sole owner of the newly created node. At the end of the scope, child is automatically destroyed, and as it is the last owner, the node is destroyed as well.

When the weak pointer in m_Children is locked later, the result is an empty shared pointer since the pointed node no longer exists.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • OK, this make perfect sense. So the solution would be to make it the other way around? Meaning the parent should be the weak pointer and the children the shared pointers? (I guess, this is what @molbdnilo pointed out in the comment above) – Roland Deschain Sep 01 '21 at 13:54
  • @RolandDeschain Yes, parent typically owns its children. You don't seem to have a parent pointer in the example, but if you did use shared ownership for children, then you could use a weak parent pointer. – eerorika Sep 01 '21 at 13:57