Last active
August 29, 2015 14:06
-
-
Save 3noch/e4e35592c59c52b85043 to your computer and use it in GitHub Desktop.
Boxed
This file contains 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
#pragma once | |
#include <cassert> | |
#include <memory> | |
/* | |
* A non-null shared pointer to some value (i.e. a shared reference). | |
* | |
* A Boxed value behaves like a shared pointer in terms of lifetime, but like also like a reference | |
* in that it cannot be null. | |
* | |
* This uses reference counting, so any danger of shared pointers also applies to Boxed. | |
* | |
* See http://en.wikipedia.org/wiki/Object_type_(object-oriented_programming)#Boxing | |
*/ | |
template<typename T> | |
class Boxed : public FunctionObject<T &> | |
{ | |
struct NotAType { int forBool; }; | |
public: | |
typedef T element_type; | |
typedef T Value; | |
typedef T & result_type; | |
struct NullPointerAssignment : std::exception {}; | |
/* | |
* Default constructs the value type into a new box. | |
*/ | |
Boxed() | |
: p_(std::make_shared<T>()) {} | |
Boxed(const Boxed & r) : p_(r.p_) { assert(p_); } | |
template<typename Y> | |
Boxed(const Boxed<Y> & r, typename std::enable_if<std::is_convertible<Y*, T*>::value, NotAType>::type = NotAType()) | |
: p_(r.p_) { assert(p_); } | |
inline operator T &() const { assert(p_); return *p_; } | |
inline T & operator*() const { assert(p_); return *p_; } | |
inline T & operator()() const { assert(p_); return *p_; } | |
inline T * get() const { assert(p_); return p_.get(); } | |
inline T * operator->() const { assert(p_); return p_.get(); } | |
Boxed & operator=(const Boxed & r) | |
{ | |
p_ = r.p_; | |
assert(p_); | |
return *this; | |
} | |
template<typename Y> | |
typename std::enable_if<std::is_convertible<Y*, element_type*>::value, Boxed&>::type | |
operator=(const Boxed<Y> & r) | |
{ | |
p_ = r.p_; | |
assert(p_); | |
return *this; | |
} | |
private: | |
std::shared_ptr<T> p_; | |
/** | |
* Fast constructor from a shared pointer that *assumes* a non-null pointer. | |
* | |
* This should never be made public and only called from friends who understand the assumptions. | |
*/ | |
Boxed(std::shared_ptr<T> p) : p_(p) { assert(p_); } | |
/** | |
* Takes ownership of an existing shared pointer and provides a Boxed version of it. Throws if given null. | |
* | |
* After this conversion, the caller must take care not to modify/delete the pointer directly. | |
* | |
* TODO: Make public only if needed. | |
*/ | |
template<typename Y, class = typename std::enable_if<std::is_convertible<Y*, element_type*>::value>::type> | |
Boxed(std::shared_ptr<Y> p) | |
: p_(p) | |
{ | |
if (!p_) { throw NullPointerAssignment(); } | |
} | |
/** | |
* Takes ownership of a pointer and converts it to a box. Throws if given null. | |
* | |
* After this conversion, the caller must take care not to modify/delete the pointer directly. | |
* | |
* TODO: Make public only if needed. | |
*/ | |
template<typename Y, class = typename std::enable_if<std::is_convertible<Y*, element_type*>::value>::type> | |
Boxed(Y * p) | |
: p_(p) | |
{ | |
if (!p_) { throw NullPointerAssignment(); } | |
} | |
// TODO: Make public only if needed. | |
template<typename Y> | |
Boxed(const std::weak_ptr<Y> & r, typename std::enable_if<std::is_convertible<Y*, T*>::value, NotAType>::type = NotAType()) | |
: p_(r.lock()) | |
{ | |
if (!p_) { throw NullPointerAssignment(); } | |
} | |
/* | |
* Reassigns the box to another value by taking ownership of the given pointer. Throws if given null. | |
* | |
* If the old value is no longer in use, it will be deleted. | |
* After this conversion, the caller must take care not to modify/delete the pointer directly. | |
* | |
* TODO: Make public only if needed. | |
*/ | |
template<typename Y> | |
typename std::enable_if<std::is_convertible<Y*, element_type*>::value, Boxed&>::type | |
operator=(Y * p) | |
{ | |
if (!p) { throw NullPointerAssignment(); } | |
p_ = p; | |
} | |
template<typename Y> friend class Boxed; | |
template<typename Y, typename... Args> friend Boxed<Y> box(Args&&... args); | |
template<typename Y> friend class EnableBoxedFromThis; | |
}; | |
/** | |
* Build a new box. Use this like you would @c std::make_shared(). | |
*/ | |
template<typename T, typename... Args> | |
inline Boxed<T> box(Args&&... args) { return std::make_shared<T>(std::forward<Args>(args)...); } | |
/* | |
* Just like @c std::enable_shared_from_this but for Boxed. | |
*/ | |
template<typename T> | |
class EnableBoxedFromThis : public std::enable_shared_from_this<T> | |
{ | |
protected: | |
Boxed<T> boxedFromThis() { return shared_from_this(); } | |
Boxed<const T> boxedFromThis() const { return shared_from_this(); } | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment