#include <list>
#include <memory>
#include <functional>
#include <iostream>

template<typename TObject, typename... CtorArgs>
class GenericPool {
public:
	typedef std::shared_ptr<TObject> TObjectPtr;

	explicit GenericPool(CtorArgs... args)
	{
		m_creator = [=]() { return std::make_shared<TObject>(args...); };
	}

	~GenericPool()
	{
	}

	TObjectPtr getObject()
	{
		if (!m_availables.empty()) {
			auto pooled = m_availables.front();
			m_availables.pop_front();
			return pooled;
		}

		m_availables.push_back(m_creator());
		return m_availables.back();
	}

	void release(TObjectPtr &objPtr)
	{
		m_availables.push_back(objPtr);
		m_inUse.remove(objPtr);
		objPtr.reset();
	}

private:
	std::function<TObjectPtr()> m_creator;

	std::list<TObjectPtr> m_availables;
	std::list<TObjectPtr> m_inUse;
};

struct YobaBase {
	YobaBase(int num, char sym) : m_num(num), m_sym(sym) {}
	virtual ~YobaBase() {}

	int m_num;
	char m_sym;
};

struct YobaDerived : YobaBase {
	YobaDerived(int num, char sym, float fnum) : YobaBase(num, sym), m_fnum(fnum) {}

	float m_fnum;
};

int main()
{
	typedef GenericPool<YobaBase, int, char> YobaBasePool;
	typedef GenericPool<YobaDerived, int, char, float> YobaDerivedPool;

	YobaBasePool ybPool(0, 'a');
	YobaDerivedPool ydPool(5, 'b', 0.0f);

	auto yb = ybPool.getObject();
	auto yd = ydPool.getObject();

	std::cout << "Yoba base (num = " << yb->m_num << ", sym = " << yb->m_sym << ")" << std::endl;
	std::cout << "Yoba derived (num = " << yd->m_num << ", sym = " 
			  << yd->m_sym << ", fnum = " << yd->m_fnum << ")" << std::endl;

	ybPool.release(yb);
	ydPool.release(yd);

	return 0;
}