You're right, it's simpler to just use a shared_ptr, and so I think I will.
But for academic reasons alone, is there a simpler way to achieve what's below?
Code:
#include <memory>
#include <condition_variable>
#include <atomic>
#include <mutex>
#include <thread>
template <typename T>
struct default_deleter
{
void operator() (T *obj) const
{
delete obj;
}
};
template <typename T, unsigned n>
struct default_deleter<T[n]>
{
void operator() (T(*arr)[n]) const
{
delete[] obj;
}
};
class delete_notification
{
std::mutex mutex;
std::condition_variable cond;
std::atomic<bool> isDeleted;
public:
delete_notification()
: isDeleted(false)
{}
void markDeleted()
{
if (!isDeleted.load()) {
isDeleted = true;
cond.notify_all();
}
}
void waitUntilDeleted()
{
std::unique_lock<std::mutex> lock(mutex);
while (!isDeleted.load())
cond.wait(lock);
}
};
template <typename T, typename Deleter = default_deleter<T> >
class notifying_deleter
{
delete_notification & notification;
Deleter del;
public:
explicit notifying_deleter(delete_notification & notification,
Deleter del = Deleter())
: notification(notification), del(del)
{}
void operator () (T *obj) const
{
del(obj);
notification.markDeleted();
}
};
class delete_waiter
{
delete_notification & notification;
public:
explicit delete_waiter(delete_notification & notification)
: notification(notification)
{}
~delete_waiter()
{
notification.waitUntilDeleted();
}
};
template <typename T>
class last_ptr
{
delete_notification notification;
delete_waiter waiter;
const std::shared_ptr<T> sharedPtr;
public:
template <typename Y>
explicit last_ptr(Y *ptr)
: waiter(notification),
sharedPtr(ptr, notifying_deleter<T>(notification))
{}
template <typename Y, typename Deleter>
last_ptr(Y *ptr, Deleter deleter)
: waiter(notification),
sharedPtr(ptr, notifying_deleter<T, Deleter>(notification, deleter))
{}
const std::shared_ptr<T> & shared() const
{
return sharedPtr;
}
};
int main()
{
last_ptr<int> last(new int(23));
std::thread slowPoke([&last] {
std::shared_ptr<int> ptr(last.shared());
std::this_thread::sleep_for(std::chrono::seconds(5));
});
slowPoke.detach();
std::shared_ptr<int> anotherRef(last.shared());
return 0;
}