this is quite hard to explain but I\'ll try my best. So, I have a RenderComponent, EventManager and RenderSystem. In my RenderComponents constructor, I raise a renderCompone
There's certainly nothing wrong with using std::weak_ptr
to grant access to an object that might be destroyed, that's what it was invented for. But it does necessitate that the object itself be held by a std::shared_ptr
. Not only does this mask your intent to have the object lifetime controlled by its parent, it forces dynamic allocation of the object and precludes it from being a member variable of the parent.
An alternate approach is to keep track of the pointer through a handle, and have a handle manager that tracks whether the object is alive or dead. The safest way to implement this is to make the manager a base class of the object you're tracking, that way RAII ensures it is always up to date. Here's a sample implementation of that concept. Note: untested.
template<class Derived>
class HandleBased
{
public:
typedef uint64_t handle_t;
HandleBased() : m_Handle(NextHandle())
{
Map()[m_Handle] = this;
}
~HandleBased()
{
auto it = Map().find(m_Handle);
Map().erase(it);
}
handle_t ThisHandle()
{
return m_Handle;
}
static Derived* FindPtr(handle_t h)
{
auto it = Map().find(h);
if (it == Map().end())
return null_ptr;
return static_cast<Derived*>(it->second);
}
private:
static handle_t NextHandle()
{
static handle_t next = 0;
return next++;
}
static std::unordered_map<handle_t, HandleBased*>& Map()
{
static std::unordered_map<handle_t, HandleBased*> the_map;
return the_map;
}
handle_t m_Handle;
};
And here's an example of how you'd use it:
class RenderNode : public HandleBased<RenderNode>
{
};
class RenderComponent
{
std::unique_ptr<RenderNode> node1;
RenderNode node2;
public:
void Setup(RenderSystem& rs)
{
node1 = new RenderNode;
rs.nodes.push_back(node1->ThisHandle());
rs.nodes.push_back(node2.ThisHandle());
}
};
class RenderSystem
{
public:
std::list<RenderNode::Handle> nodes;
void DoRender()
{
for (auto it = nodes.begin(); it != nodes.end(); )
{
RenderNode* p = RenderNode::FindPtr(*it);
if (p == NULL)
it = nodes.erase(it);
else
{
p->DoSomething();
++it;
}
}
}
};