Hi,
I spent today playing with object pools in C++ and got to a solution that works but have little quirks. I would like to hear opinions from other pals about this design (not if the pooling is efficient or not, but the system structure) based in some requisites I imposed at first. This are:
1) I want a centralized place for managing my object pools
2) I want my object pool to be a template.
3) As I want to have a pool manager and my pool class is templatized I need a class to inherit from. This class must be the an interface and must have 3 methods. init, newObject and deleteObject.
What I don't like from the solution I'm providing is the dynamic_casts I must do to create and object and to return an object to the pool. This is mainly the main problem I see. Another thing I don't like but I think that there's no solution (beside providing a custom allocator in the init method) is that Object pools and PoolMgr rely on some static methods that the IPoolables must implement like getClassId() and create(). What I don't like is that there's no information in the interfaces to force the user to implement in the specific classes.
Think that all this code has been written in a little rush while I was designing so it could contain bugs. I'm just interested in the idea, ussability and how classes play.
Well here is my solution. If anyone could give any idea, different aproach, or confirm you like it (I doubt it ) I would be really happy.
#IPoolable interface
//IPoolable class. All objects that want to be pooled must inherit from this
class IPoolable
{
public:
virtual void reset() = 0;
virtual void create() = 0;
};
# ObjectPool interface
class IObjectPool
{
public:
virtual void init(unsigned int initialCapacity, unsigned int chunkSize, unsigned int maxCapacity)=0;
virtual void deleteObj(IPoolable* obj)=0;
virtual IPoolable* newObj()=0;
};
#ObjectPool
//Object pool that contains specific IPoolables
template <typename T>
class ObjectPool : public IObjectPool
{
public:
ObjectPool()
{
CCLOG("CocosObjectPool created...");
}
~ObjectPool()
{
CCLOG("CocosObjectPool destroyed...");
}
virtual void init(unsigned int initialCapacity, unsigned int chunkSize, unsigned int maxCapacity);
virtual void deleteObj(IPoolable* obj);
virtual IPoolable* newObj();
protected:
private:
void _GrowStack(int growSize);
private:
int _initialCapacity=0;
int _maxCapacity=0;
int _chunkSize = 0;
std::queue<T*> _objects;
};
template <typename T>
void ObjectPool<T>::init(unsigned int initialCapacity, unsigned int chunkSize, unsigned int maxCapacity)
{
_initialCapacity = initialCapacity;
_chunkSize = chunkSize;
_maxCapacity = maxCapacity;
if (_initialCapacity != 0)
{
_GrowStack(initialCapacity);
}
}
template <typename T>
void ObjectPool<T>::_GrowStack(int growSize)
{
for (int i = 0; i < growSize; ++i)
{
_objects.push(T::create());
}
}
template <typename T>
void ObjectPool<T>::deleteObj(IPoolable* obj)
{
T* t = dynamic_cast<T*>(obj);
_objects.push(t);
}
template <typename T>
IPoolable* ObjectPool<T>::newObj()
{
if (_objects.size() == 0)
_GrowStack(_chunkSize);
T* obj = _objects.front();
_objects.pop();
return obj;
}
#PoolMgr
//PoolMgr
class PoolMgr
{
public:
PoolMgr(){};
virtual ~PoolMgr(){};
void init(){};
template <typename T> void registerType(unsigned int initialCapacity, unsigned int chunkSize, unsigned int maxCapacity);
template <typename T> T* createObj();
template <typename T> void deleteObj(IPoolable* obj);
protected:
private:
std::map<size_t, unique_ptr<IObjectPool>> _pools;
};
template <typename T> void PoolMgr::registerType(unsigned int initialCapacity, unsigned int chunkSize, unsigned int maxCapacity)
{
unique_ptr<ObjectPool<T>> op(new ObjectPool<T>());
op->init(initialCapacity,chunkSize,maxCapacity);
_pools.insert(make_pair(T::getClassId(), std::move(op)));
}
template <typename T> T* PoolMgr::createObj()
{
auto it = _pools.find(T::getClassId());
return dynamic_cast<T*> ( it->second->newObj() );
}
template <typename T> void PoolMgr::deleteObj(IPoolable* obj)
{
auto it = _pools.find(T::getClassId());
return it->second->deleteObj(obj);
}
This is how it is used from client code:
using namespace FosforeEngine;
//Using Pool manager
FosforeEngine::PoolMgr* poolMgr = new FosforeEngine::PoolMgr();
poolMgr->registerType<ZombieBunny>(5,5,10);
ZombieBunny* bunny = poolMgr->createObj<ZombieBunny>();
//Without registering the pool to a pool manager
ObjectPool<ZombieBunny>* objectPool = new ObjectPool<ZombieBunny>();
objectPool->init(5, 5, 10);
ZombieBunny* zombie=dynamic_cast<ZombieBunny*>(objectPool->newObj());
objectPool->deleteObj(zombie);
Well here you are, sorry for the long post.
Cheers.