gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > Object pooling

#32231 - Steve++ - Sun Dec 19, 2004 7:12 pm

I'm having some trouble with my object pooling code. It's not giving the expected results. Here's the code:
Code:
#include <stdio.h>

class Poolable
{
private:
   unsigned poolHandle;
protected:
   Poolable() {}
   friend class ObjectPool;
};

class ObjectPool
{
   Poolable* pool;
   Poolable** stack;
   int size;
   int tos;
public:
   void reset()
   {
      for (int i=0; i<size; ++i)
      {
         stack[i] = pool+i;
         pool[i].poolHandle = i;
      }
      tos = size-1;
   }
   
   ObjectPool(Poolable* pool, Poolable** stack, int size)
   : pool(pool), stack(stack), size(size)
   {
      reset();
   }
   
   Poolable* allocate()
   {
      return tos >= 0 ? stack[tos--] : NULL;
   }
   
   bool deallocate(Poolable* p)
   {
      if (tos < size-1)
      {
         stack[++tos] = pool+p->poolHandle;
         return true;
      }
      else
      {
         return false;
      }
   }
};

class TestObject : public Poolable
{
   int x;
public:
   TestObject() : x(0) {}
   int getx()
   {
      return x;
   }
   void setx(int x)
   {
      this->x = x;
   }
};

int main()
{
   TestObject pool[10];
   TestObject *stack[10];
   ObjectPool p(pool, (Poolable**)stack, 10);
   
   TestObject* a = (TestObject*)p.allocate();   a->setx(1);
   TestObject* b = (TestObject*)p.allocate();   b->setx(2);
   TestObject* c = (TestObject*)p.allocate();   c->setx(3);
   
   printf("%d %d %d\n", a->getx(), b->getx(), c->getx());
   // prediction: 1 2 3
   
   p.deallocate(a);
   p.deallocate(c);
   p.deallocate(b);
   
   a = (TestObject*)p.allocate();
   b = (TestObject*)p.allocate();
   c = (TestObject*)p.allocate();
   
   printf("%d %d %d\n", a->getx(), b->getx(), c->getx());
   // prediction: 2 3 1
   
   return 0;
}

The output is:
Code:

1 2 3
4 3 3

That's weird because I didn't set anything to 4. Ideas anyone?

#32258 - Steve++ - Mon Dec 20, 2004 6:12 am

As I suspected, the problem is with casting. Casting pointers without RTTI is a bad idea - although the classes are in the same heirarchy, they are different sizes; a fact not picked up with old-style pointer casting. I should have used dynamic_cast instead. But RTTI is not something I want in a GBA project, so I've taken the template approach. I wanted to do this originally, but I assumed the compiler couldn't do certain things.

The code:
Code:
#include <stdio.h>

template<class T> class ObjectPool;

template<class T>
class Poolable
{
private:
   unsigned poolHandle;
protected:
   Poolable() {}
   friend class ObjectPool<T>;
};

template<class T>
class ObjectPool
{
   T* pool;
   T** stack;
   int size;
   int tos;
public:
   void reset()
   {
      for (int i=0; i<size; ++i)
      {
         stack[i] = pool+i;
         pool[i].poolHandle = i;
      }
      tos = size-1;
   }
   
   ObjectPool(T* pool, T** stack, int size)
   : pool(pool), stack(stack), size(size)
   {
      reset();
   }
   
   T* allocate()
   {
      if (tos >= 0)
      {
         return stack[tos--];
      }
      else
      {
         return NULL;
      }
   }
   
   bool deallocate(T* p)
   {
      if (tos < size-1)
      {
         stack[++tos] = pool+p->poolHandle;
         return true;
      }
      else
      {
         return false;
      }
   }
};

class TestObject : public Poolable<TestObject>
{
   int x;
public:
   TestObject() : x(0) {}
   int getx()
   {
      return x;
   }
   void setx(int x)
   {
      this->x = x;
   }
};

int main()
{
   TestObject pool[10];
   TestObject *stack[10];
   ObjectPool<TestObject> p(pool, stack, 10);
   
   TestObject* a = p.allocate();   a->setx(1);
   TestObject* b = p.allocate();   b->setx(2);
   TestObject* c = p.allocate();   c->setx(3);
   
   printf("%d %d %d\n", a->getx(), b->getx(), c->getx());
   // prediction: 1 2 3
   
   p.deallocate(a);
   p.deallocate(c);
   p.deallocate(b);
   
   a = p.allocate();
   b = p.allocate();
   c = p.allocate();
   
   printf("%d %d %d\n", a->getx(), b->getx(), c->getx());
   // prediction: 2 3 1
   
   return 0;
}


The first pleasant surprise was that the compiler lets us write a template and assume the class parameter, T, will have a certain member. The compiler only throws an error if T doesn't supply that member.

The next pleasant surprise was this line:
Code:
class TestObject : public Poolable<TestObject>
It seems a bit circular, but the compiler said nothing about it.

The program now gives the expected output and this approach seems a lot cleaner anyway (no casting). Now onto the linked list class that will consume nodes from a pool. :)