Skip to content

Instantly share code, notes, and snippets.

@AnthonySuper
Created October 3, 2017 21:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AnthonySuper/57633b0a5ffdedc2ee36b9ebe3d21f16 to your computer and use it in GitHub Desktop.
Save AnthonySuper/57633b0a5ffdedc2ee36b9ebe3d21f16 to your computer and use it in GitHub Desktop.
namespace nmh {
template<typename T>
class value_ptr {
protected:
T* pointedAt;
template<typename O>
friend class value_ptr;
struct value_deleter {
virtual void _delete(T* in) = 0;
virtual value_deleter* _clone() = 0;
virtual T* clonePointed(T*) = 0;
virtual ~value_deleter() {}
};
value_deleter *deleter;
template<typename Del = std::default_delete<T>>
struct value_deleter_impl : public value_deleter {
Del del;
value_deleter_impl(const Del& in) : del(in) {}
value_deleter_impl(const value_deleter_impl<Del>&) = default;
virtual void _delete(T* in) override {
if(in == nullptr) {
throw std::runtime_error("Deleting nullptr");
}
del(in);
}
virtual T* clonePointed(T* in) override {
return new T(*in);
}
virtual value_deleter* _clone() override {
return new value_deleter_impl<Del>(*this);
}
};
template<typename Other,
typename = std::enable_if_t<std::is_base_of_v<Other, T>>>
struct converting_deleter : public value_ptr<Other>::value_deleter {
value_deleter* del;
converting_deleter(value_deleter* _del) : del(_del) {}
virtual void _delete(Other* in) override {
// by the contract of converting_deleter, this is legitimate:
del->_delete(static_cast<T*>(in));
}
virtual typename value_ptr<Other>::value_deleter* _clone() override {
return new converting_deleter(del->_clone());
}
virtual Other* clonePointed(Other *in) override {
return del->clonePointed(static_cast<T*>(in));
}
virtual ~converting_deleter() {
delete del;
}
};
public:
value_ptr(value_ptr<T> const & other) {
pointedAt = other.rawClone();
deleter = other->deleter._clone();
}
value_ptr(value_ptr<T>& other) {
pointedAt = other.rawClone();
deleter = other.deleter->_clone();
}
template<typename O>
value_ptr(const value_ptr<O>& o,
typename std::enable_if_t<std::is_base_of_v<T, O>,
int> v = 0) {
pointedAt = new O(*o.pointedAt);
deleter = new typename value_ptr<O>::template converting_deleter<T>(o.deleter->_clone());
}
template<typename O>
value_ptr(value_ptr<O>& o,
typename std::enable_if_t<std::is_base_of_v<T, O>,
int> v = 0) {
pointedAt = new O(*o.pointedAt);
deleter = new typename value_ptr<O>::template converting_deleter<T>(o.deleter->_clone());
}
template<typename O>
value_ptr(value_ptr<O>&& o,
typename std::enable_if_t<std::is_base_of_v<T, O>,
int> v = 0) {
pointedAt = o.pointedAt;
deleter = new typename value_ptr<O>::template converting_deleter<T>(o.deleter);
o.pointedAt = nullptr;
o.deleter = nullptr;
}
// note that assigning to a moved-from object is now undefined
// behavior, as it will dereference null
value_ptr(value_ptr<T>&& other) {
pointedAt = other.pointedAt;
deleter = other.deleter;
other.pointedAt = nullptr;
other.deleter = nullptr;
}
value_ptr(T* _pointedAt) :
pointedAt(_pointedAt), deleter(new value_deleter_impl<std::default_delete<T>>(std::default_delete<T>()))
{}
template<typename Del>
value_ptr(T* _pa, const Del& d) :
pointedAt(_pa),
deleter(new value_deleter_impl<Del>(d))
{}
value_ptr<T>& operator=(const value_ptr<T>& other) {
if(pointedAt != nullptr) {
deletePointed();
}
pointedAt = other.rawClone();
if(deleter != nullptr) delete deleter;
deleter = other.deleter;
return *this;
}
value_ptr<T>& operator=(value_ptr<T>&& other) {
if(pointedAt != nullptr) deletePointed();
if(deleter != nullptr) delete deleter;
pointedAt = other.pointedAt;
deleter = other.deleter;
other.pointedAt = nullptr;
other.deleter = nullptr;
return *this;
}
template<typename O>
typename std::enable_if_t<std::is_base_of_v<T, O>,
value_ptr<T>&>
operator=(const value_ptr<O>& other) {
if(pointedAt != nullptr) deletePointed();
pointedAt = other.rawClone();
deleter = new typename value_ptr<O>::template converting_deleter<T>(other.deleter->_clone());
return *this;
}
template<typename O>
typename std::enable_if_t<std::is_base_of_v<T, O>,
value_ptr<T>&>
operator=(value_ptr<O>&& other) {
if(pointedAt != nullptr) deletePointed();
if(deleter != nullptr) delete deleter;
pointedAt = other.pointedAt;
deleter = new typename value_ptr<O>::template converting_deleter<T>(other.deleter);
other.pointedAt = nullptr;
other.deleter = nullptr;
return *this;
}
T* operator->() {
return pointedAt;
}
~value_ptr() {
if(pointedAt == nullptr) {
// nothing for now!
}
else {
deletePointed();
}
if(deleter != nullptr) {
delete deleter;
}
}
private:
void deletePointed() {
if(deleter == nullptr) {
throw std::runtime_error("Probably impossible?");
}
deleter->_delete(pointedAt);
}
T* rawClone() {
return deleter->clonePointed(pointedAt);
}
friend
void swap(value_ptr<T>&, value_ptr<T>&);
};
template<typename T>
void swap(value_ptr<T>& a, value_ptr<T>& b) {
auto p = a.pointedAt;
auto d = a.deleter;
a.pointedAt = b.pointedAt;
a.deleter = b.deleter;
b.pointedAt = p;
b.deleter = d;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment