Skip to content

Instantly share code, notes, and snippets.

@3noch
Last active August 29, 2015 14:06
Show Gist options
  • Save 3noch/e4e35592c59c52b85043 to your computer and use it in GitHub Desktop.
Save 3noch/e4e35592c59c52b85043 to your computer and use it in GitHub Desktop.
Boxed
#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