I\'m having fun with c++-ideas, and got a little stuck with this problem.
I would like a LIFO class that manages a pool of resources.
When a resource is
The implementation uses unique_ptr with a custom deleter that returns objects to the pool. Both acquire and release are O(1). Additionally, unique_ptr with custom deleters can be implicitly converted to shared_ptr.
template
class SharedPool
{
public:
using ptr_type = std::unique_ptr >;
SharedPool() {}
virtual ~SharedPool(){}
void add(std::unique_ptr t) {
pool_.push(std::move(t));
}
ptr_type acquire() {
assert(!pool_.empty());
ptr_type tmp(pool_.top().release(),
[this](T* ptr) {
this->add(std::unique_ptr(ptr));
});
pool_.pop();
return std::move(tmp);
}
bool empty() const {
return pool_.empty();
}
size_t size() const {
return pool_.size();
}
private:
std::stack > pool_;
};
Example usage:
SharedPool pool;
pool.add(std::unique_ptr(new int(42)));
pool.add(std::unique_ptr(new int(84)));
pool.add(std::unique_ptr(new int(1024)));
pool.add(std::unique_ptr(new int(1337)));
// Three ways to express the unique_ptr object
auto v1 = pool.acquire();
SharedPool::ptr_type v2 = pool.acquire();
std::unique_ptr > v3 = pool.acquire();
// Implicitly converted shared_ptr with correct deleter
std::shared_ptr v4 = pool.acquire();
// Note that adding an acquired object is (correctly) disallowed:
// pool.add(v1); // compiler error
You might have caught a severe problem with this implementation. The following usage isn't unthinkable:
std::unique_ptr< SharedPool > pool( new SharedPool );
pool->add(std::unique_ptr(new Widget(42)));
pool->add(std::unique_ptr(new Widget(84)));
// [Widget,42] acquired(), and released from pool
auto v1 = pool->acquire();
// [Widget,84] is destroyed properly, together with pool
pool.reset(nullptr);
// [Widget,42] is not destroyed, pool no longer exists.
v1.reset(nullptr);
// Memory leak
We need a way to keep alive information necessary for the deleter to make the distinction
One way of doing this (suggested by T.C.), is having each deleter keep a weak_ptr to shared_ptr member in SharedPool. This lets the deleter know if the pool has been destroyed.
template
class SharedPool
{
private:
struct External_Deleter {
explicit External_Deleter(std::weak_ptr* > pool)
: pool_(pool) {}
void operator()(T* ptr) {
if (auto pool_ptr = pool_.lock()) {
try {
(*pool_ptr.get())->add(std::unique_ptr{ptr});
return;
} catch(...) {}
}
std::default_delete{}(ptr);
}
private:
std::weak_ptr* > pool_;
};
public:
using ptr_type = std::unique_ptr;
SharedPool() : this_ptr_(new SharedPool*(this)) {}
virtual ~SharedPool(){}
void add(std::unique_ptr t) {
pool_.push(std::move(t));
}
ptr_type acquire() {
assert(!pool_.empty());
ptr_type tmp(pool_.top().release(),
External_Deleter{std::weak_ptr*>{this_ptr_}});
pool_.pop();
return std::move(tmp);
}
bool empty() const {
return pool_.empty();
}
size_t size() const {
return pool_.size();
}
private:
std::shared_ptr* > this_ptr_;
std::stack > pool_;
};