Simple, efficient weak pointer that is set to NULL when target memory is deallocated
Asked Answered
Y

2

9

Is there a simple, efficient weak/guarded pointer? I need multiple pointers to the same object that are all automatically set to NULL when the object is deleted. There is one "master" pointer that is always used to delete the object, but there can be several other pointers that reference the same object.

Here are some solutions that don't quite match my needs:

  • QPointer: I am not developing a QT app; I do not wish to include this libary/derive from QObject.
  • boost::weak_ptr: an exception is thrown when accessing a deallocated object. Too expensive for my situation: it should be normal to test a weak pointer; I plan to do some manual clean-up when a weak pointer is no longer valid. update:weak_ptr can be tested without throwing exceptions
  • Low-Overhead Weak Pointers: This is very close to what I am looking for, except I don't like the fact "This scheme is only guaranteed to work as long as you don’t allocate 2**sizeof(int) times in the same location."

Why I need these weak/guarded pointers: I have a game with a list of game objects. Some objects are dependent on others, for example a debug/stats object that is associated with a game entity. The debug/status object displays useful info about the game entity, but it only makes sense while the game entity exists. So if the game entity is deleted, the debug/stats object should realize this and delete itself. (Another idea is a tracking missile: instead of deleting itself, it may search for a new target.)

I wish to keep the debug/stats logic separate from the game entity. The game entity should not have to know a debug/stats object is attached to it. While I'd prefer an answer for weak/guarded pointers, I also welcome different ways to approach my specific task. I am thinking I may have to implement a game object manager that tracks object lifetimes and uses handles instead of raw pointers to memory addresses.

I am developing in C++.

Younger answered 9/12, 2009 at 19:15 Comment(3)
A general question would be, "is Qt the right choice for developing a game?"Clementineclementis
Boost's weak pointer allows you to test if it is valid. It only throws an exception if you directly try to dereference it -- similar to what happens if you try to dereference a null pointer.Latinity
C++11 has std::weak_ptr. Looks similar to the one from Boost.Kurdistan
P
17

You can use the lock() member of boost::weak_ptr to be able to test (then use) the value of the weak_ptr without dealing with exceptions.

Pacifist answered 9/12, 2009 at 19:19 Comment(1)
Might look at std::weak_ptr now the c++11 is out.Crowns
S
10

This is a common thing in game development. Typically a system of object handles is used rather than Boost weak pointers, because we need the underlying lookup table to be constant memory and because sometimes we need some additional information or guarantees that Boost hasn't got.

The usual approach is to use an elaboration on pointers to pointers. An entity is referred to by handle rather than by pointer. The handle is an index into a big array of pointers to entities. When an entity dies, it NULLs out the pointer in its entity table.

struct handle_t
{
   uint32 serialnumber;  // this is a GUID for each entity; it increases 
                         // monotonically over the life of the process
   uint   entityindex;
   inline Entity *Get();
}

struct entityinfo_t
{
   Entity *pEntity;  // an entity's destructor NULLs this out on deletion
   uint32  serialnumber;
}

entityinfo_t g_EntityTable[MAX_ENTITIES];

Entity *handle_t::Get() 
{
  entityinfo_t &info = g_EntityTable[entityIndex];
  if ( serialnumber == info.serialnumber )  
  {
     return info.pEntity;
  }
  else
  {
      return NULL;
  }
}

The serial number is necessary because the array is of constant size -- eventually, you will need to recycle entity table entries, and there's a possibility that you might store a handle to, say, index #743, long enough that the object gets deleted and cell #743 reused for something else. If you simply had a pointer to a list of pointers, you would end up having a handle that points to an entirely different object rather than going NULL. So, we give each entity a globally unique number and store that in the handle as well.

You could use a std vector or a map or a dictionary or some other kind of data structure for the entity table, of course, but our requirements have typically been for constant memory, cache coherency, and absolute maximal performance (since handle_t::Get() gets called thousands of times per frame).

Symmetry answered 9/12, 2009 at 20:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.