views:

237

answers:

6

Per-frame I need to allocate some data that needs to stick around until the end of the frame.

Currently, I'm allocating the data off a different memory pool that allows me to mark it with the frame count. At the end of the frame, I walk the memory pool and delete the memory that was allocated in a particular frame.

The problem I'm running into is that in order to keep a hold on the data, I have to place it in a structure thusly:

struct FrameMemory
{
    uint32 frameIndex;
    bool allocatedType; //0 = new(), 1 = new[]
    void* pMemPtr;
}

So later, when i get around to freeing the memory, it looks something like this:

{
for(all blocks)
 if(block[i].frameIndex == targetIndex)
     if(block[i].allocatedType == 0)
          delete block[i].pMemPtr;
     else if (block[i].allocatedType ==1)
          delete[] block[i].pMemPtr;
}

The issue is that, because I have to overload the pointer to the memory as a void*, the DELETE operator doesn't properly DELETE the memory as its' native base type. IE the destructor NEVER gets called for the object.

I've attempted to find ways to use smart-pointer templated objects for the solution, but in order to do that, I have to overload the templated class to a non-templated base-type, which makes deletion even more difficult.

Does anyone have a solution to a problem like this?

+6  A: 
class Destructable
{
public:
   virtual ~Destructable() {}
};

Instead of void *, store Destructable * in the pool. Make objects allocated from the pool inherit from Destructable.

Alternatively, override the operators new and delete for the relevant classes. Make them use the pool. Delete the object as soon as the CPU is done with it, in the regular way, in the code that owns it and hence knows its correct type; since the pool will not reuse the memory until it sees the appropriate frame end, whatever asynchronous hardware required you to lag garbage collection in this way can still do its thing.

moonshadow
The problem with that, is that it makes it difficult to allocate types from the pool that don't inherit that base type. For instance if I wanted to allocate an array of Ints from the pool, I could no longer do that, unless I created a new wrapper for it.
ddigit
Then you'll have to create a wrapper. Or use a separate pool for that type. The pool itself could be a class template to contain whatever type of object you need. C++ is a statically typed language. Trying to force it to behave like a dynamically typed language is a bad idea. I know, because I've tried. :)
Dima
Another problem with this approach is that it forces classes to be virtual when they otherwise wouldn't have to.
keraba
A: 

The only way I could think of to do that would be to add a type entry to the FrameMemory struct, then use that to properly cast the memory for the delete. For example, if you have memory of type foo, you could have something like:

if (block[i].BlockType == BLOCKTYPE_FOO)
{
    foo *theMemory = (foo *)block[i].pMemPtr;

    delete theMemory;
}

Note that this can be an *extremely* dangerous operation if you do it wrong.

jfawcett
A: 

If you are mean stack frame (i.e. inside function) you can try to use alloca()

+4  A: 

If you don't want to force all the objects to inherit from Destructible, you can store a pointer to a deleter function (or functor) along with the pointer to the data itself. The client code is responsible for providing a function that knows how to delete the data correctly, typically something like:

void xxx_deleter(void *data) { 
    xxx *ptr = static_cast<xxx *>(data);
    delete ptr;
}

Though the deleter will usually be a lot like the above, this also gives the client the option of storing complex data structures and still getting them deleted correctly.

Jerry Coffin
This approach will work quite well. It allows for the destructors to be called correctly, and allows default destructors to be generated for base types. This allows me to not have to overload everything with a base class, and keeps me from managing multiple pools. THANK YOU.
ddigit
This is similar to the approach taken by boost::shared_ptr. If you're feeling adventurous, you might want to take a look at its implementation, since there are a few corner cases that it takes care to treat properly. http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_ptr.htm
Edward Loper
A: 

First thing that I can think of is using boost::shared_ptr<void> (for the non-array version, some work may be required to adapt it for the array version) as the pointer type. And I think that should take care of mostly every detail. Whenever the frame is destroyed the memory will be appropriately deleted:

struct FrameMemory
{
    uint32 frameIndex;
//    bool allocatedType; //0 = new(), 1 = new[] only works with instances, not arrays
    boost::shared_ptr<void> pMemPtr;
};

If you want to implement something similar manually, you can use a 'deleter' function pointer to handle the deletion of the objects, instead of calling delete directly. Here is a rough approach to how you could modify your code:

// helper deleter functions
template <typename T>
void object_deleter( void *p ) {
   delete static_cast<T*>(p);
}
template <typename T>
void array_deleter( void *p ) {
   delete [] static_cast<T*>(p);
}

class FrameMemory
{
public:
    const uint32 frameIndex;
    void* pMemPtr;
private:
    void (*deleter)(void*);
public:
    template <typename T>
    FrameMemory( uint32 frame, T* memory, bool isarray = false )
       : frameIndex(frame), pMemPtr(memory), 
         deleter( isarray? array_deleter<T> : object_deleter<T> )
    {}
    void delete() {
       deleter(pMemPtr)
    }
};
struct X;
void usage()
{
    {
       FrameMemory f( 1, new X );
       f.delete();
    }
    {
       FrameMemory f( 1, new x[10], true );
       f.delete();
    }
}

I would modify it further so that instead of having to call FrameMemory::delete() that was performed in the destructor, but that would take more time than I have right now to do correctly (that is, deciding how copies are to be handled and so on...

David Rodríguez - dribeas
A: 

I would do something like this:

struct FrameMemoryBase
{
    uint32 frameIndex;
    bool allocatedType; //0 = new(), 1 = new[]
    virtual void Free() = 0;
};

template <typename T>
struct FrameMemory : public FrameMemoryBase
{
  void Free()
  {
     if(allocatedType == 0)
         delete pMemPtr;
     else if (allocatedType ==1)
         delete[] pMemPtr;
  }

  T *pMemPtr;
};

which you would use via:

{
for(all blocks)
    if(block[i].frameIndex == targetIndex)
        block[i].Free(); 
}    

If you also free the FrameMemory struct you could just change Free to a virtual destructor. I'm not sure this is what you're looking for since I don't understand what "I've attempted to find ways to use smart-pointer templated objects for the solution, but in order to do that, I have to overload the templated class to a non-templated base-type, which makes deletion even more difficult." means, but I hope this is helpful.

This requires that the memory management code somehow have access to the declarations of what you wish to free, but I don't think there's any way around that, assuming you want destructors called, which you explicitly do.

Edward
Hmmm...I wrote this without thinking it through enough. I assume you're keeping an array of blocks. I started thinking about how to modify my answer to make that possible, and realized dribeas already provided the answer.
Edward