Created
October 3, 2017 21:42
-
-
Save AnthonySuper/57633b0a5ffdedc2ee36b9ebe3d21f16 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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