445 lines
9.6 KiB
C
445 lines
9.6 KiB
C
|
/*
|
||
|
|
||
|
Copyright (c) 2016 Jonathan B. Coe
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||
|
this software and associated documentation files (the "Software"), to deal in
|
||
|
the Software without restriction, including without limitation the rights to
|
||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||
|
subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in all
|
||
|
copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#ifndef POLY_H
|
||
|
#define POLY_H
|
||
|
|
||
|
#include <cassert>
|
||
|
#include <exception>
|
||
|
#include <memory>
|
||
|
#include <type_traits>
|
||
|
#include <typeinfo>
|
||
|
|
||
|
namespace detail
|
||
|
{
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
// Implementation detail classes
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
template <class T>
|
||
|
struct default_copy
|
||
|
{
|
||
|
T* operator()(const T& t) const
|
||
|
{
|
||
|
return new T(t);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
struct default_delete
|
||
|
{
|
||
|
void operator()(const T* t) const
|
||
|
{
|
||
|
delete t;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
struct control_block
|
||
|
{
|
||
|
virtual ~control_block() = default;
|
||
|
|
||
|
virtual std::unique_ptr<control_block> clone() const = 0;
|
||
|
|
||
|
virtual T* ptr() = 0;
|
||
|
};
|
||
|
|
||
|
template <class T, class U = T>
|
||
|
class direct_control_block : public control_block<T>
|
||
|
{
|
||
|
static_assert(!std::is_reference<U>::value, "");
|
||
|
U u_;
|
||
|
|
||
|
public:
|
||
|
template <class... Ts>
|
||
|
explicit direct_control_block(Ts&&... ts) : u_(U(std::forward<Ts>(ts)...))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<control_block<T>> clone() const override
|
||
|
{
|
||
|
return std::make_unique<direct_control_block>(*this);
|
||
|
}
|
||
|
|
||
|
T* ptr() override
|
||
|
{
|
||
|
return std::addressof(u_);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <class T, class U, class C = default_copy<U>,
|
||
|
class D = default_delete<U>>
|
||
|
class pointer_control_block : public control_block<T>, public C
|
||
|
{
|
||
|
std::unique_ptr<U, D> p_;
|
||
|
|
||
|
public:
|
||
|
explicit pointer_control_block(U* u, C c = C{}, D d = D{})
|
||
|
: C(std::move(c)), p_(u, std::move(d))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
explicit pointer_control_block(std::unique_ptr<U, D> p, C c = C{})
|
||
|
: C(std::move(c)), p_(std::move(p))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<control_block<T>> clone() const override
|
||
|
{
|
||
|
assert(p_);
|
||
|
return std::make_unique<pointer_control_block>(
|
||
|
C::operator()(*p_), static_cast<const C&>(*this), p_.get_deleter());
|
||
|
}
|
||
|
|
||
|
T* ptr() override
|
||
|
{
|
||
|
return p_.get();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <class T, class U>
|
||
|
class delegating_control_block : public control_block<T>
|
||
|
{
|
||
|
std::unique_ptr<control_block<U>> delegate_;
|
||
|
|
||
|
public:
|
||
|
explicit delegating_control_block(std::unique_ptr<control_block<U>> b)
|
||
|
: delegate_(std::move(b))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<control_block<T>> clone() const override
|
||
|
{
|
||
|
return std::make_unique<delegating_control_block>(delegate_->clone());
|
||
|
}
|
||
|
|
||
|
T* ptr() override
|
||
|
{
|
||
|
return static_cast<T*>(delegate_->ptr());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
} // end namespace detail
|
||
|
|
||
|
class bad_poly_construction : std::exception
|
||
|
{
|
||
|
public:
|
||
|
bad_poly_construction() noexcept = default;
|
||
|
|
||
|
const char* what() const noexcept override
|
||
|
{
|
||
|
return "Dynamic and static type mismatch in poly "
|
||
|
"construction";
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
class poly;
|
||
|
|
||
|
template <class T>
|
||
|
struct is_poly : std::false_type
|
||
|
{
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
struct is_poly<poly<T>> : std::true_type
|
||
|
{
|
||
|
};
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// `poly` class definition
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
template <class T>
|
||
|
class poly
|
||
|
{
|
||
|
static_assert(!std::is_union<T>::value, "");
|
||
|
static_assert(std::is_class<T>::value, "");
|
||
|
|
||
|
template <class U>
|
||
|
friend class poly;
|
||
|
|
||
|
template <class T_, class U, class... Ts>
|
||
|
friend poly<T_> make_poly(Ts&&... ts);
|
||
|
template <class T_, class... Ts>
|
||
|
friend poly<T_> make_poly(Ts&&... ts);
|
||
|
template <class T_, class U>
|
||
|
friend poly<T_> poly_cast(poly<U> p);
|
||
|
|
||
|
T* ptr_ = nullptr;
|
||
|
std::unique_ptr<detail::control_block<T>> cb_;
|
||
|
|
||
|
public:
|
||
|
//
|
||
|
// Destructor
|
||
|
//
|
||
|
|
||
|
~poly() = default;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Constructors
|
||
|
//
|
||
|
|
||
|
poly()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
template <class U, class C = detail::default_copy<U>,
|
||
|
class D = detail::default_delete<U>,
|
||
|
class V = std::enable_if_t<std::is_convertible<U*, T*>::value>>
|
||
|
explicit poly(U* u, C copier = C{}, D deleter = D{})
|
||
|
{
|
||
|
if (!u)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (std::is_same<D, detail::default_delete<U>>::value &&
|
||
|
std::is_same<C, detail::default_copy<U>>::value &&
|
||
|
typeid(*u) != typeid(U))
|
||
|
|
||
|
throw bad_poly_construction();
|
||
|
|
||
|
std::unique_ptr<U, D> p(u, std::move(deleter));
|
||
|
|
||
|
cb_ = std::make_unique<detail::pointer_control_block<T, U, C, D>>(
|
||
|
std::move(p), std::move(copier));
|
||
|
ptr_ = u;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Copy-constructors
|
||
|
//
|
||
|
|
||
|
poly(const poly& p)
|
||
|
{
|
||
|
if (!p)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
auto tmp_cb = p.cb_->clone();
|
||
|
ptr_ = tmp_cb->ptr();
|
||
|
cb_ = std::move(tmp_cb);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Move-constructors
|
||
|
//
|
||
|
|
||
|
poly(poly&& p) noexcept
|
||
|
{
|
||
|
ptr_ = p.ptr_;
|
||
|
cb_ = std::move(p.cb_);
|
||
|
p.ptr_ = nullptr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Converting constructors
|
||
|
//
|
||
|
|
||
|
template <class U,
|
||
|
class V = std::enable_if_t<!std::is_same<T, U>::value &&
|
||
|
std::is_convertible<U*, T*>::value>>
|
||
|
poly(const poly<U>& p)
|
||
|
{
|
||
|
poly<U> tmp(p);
|
||
|
ptr_ = tmp.ptr_;
|
||
|
cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
|
||
|
std::move(tmp.cb_));
|
||
|
}
|
||
|
|
||
|
template <class U,
|
||
|
class V = std::enable_if_t<!std::is_same<T, U>::value &&
|
||
|
std::is_convertible<U*, T*>::value>>
|
||
|
poly(poly<U>&& p)
|
||
|
{
|
||
|
ptr_ = p.ptr_;
|
||
|
cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
|
||
|
std::move(p.cb_));
|
||
|
p.ptr_ = nullptr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Forwarding constructor
|
||
|
//
|
||
|
|
||
|
template <class U, class V = std::enable_if_t<
|
||
|
std::is_convertible<std::decay_t<U>*, T*>::value &&
|
||
|
!is_poly<std::decay_t<U>>::value>>
|
||
|
poly(U&& u)
|
||
|
: cb_(std::make_unique<
|
||
|
detail::direct_control_block<T, std::decay_t<U>>>(
|
||
|
std::forward<U>(u)))
|
||
|
{
|
||
|
ptr_ = cb_->ptr();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Assignment
|
||
|
//
|
||
|
|
||
|
poly& operator=(const poly& p)
|
||
|
{
|
||
|
if (std::addressof(p) == this)
|
||
|
{
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
if (!p)
|
||
|
{
|
||
|
cb_.reset();
|
||
|
ptr_ = nullptr;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
auto tmp_cb = p.cb_->clone();
|
||
|
ptr_ = tmp_cb->ptr();
|
||
|
cb_ = std::move(tmp_cb);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Move-assignment
|
||
|
//
|
||
|
|
||
|
poly& operator=(poly&& p) noexcept
|
||
|
{
|
||
|
if (std::addressof(p) == this)
|
||
|
{
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
cb_ = std::move(p.cb_);
|
||
|
ptr_ = p.ptr_;
|
||
|
p.ptr_ = nullptr;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Modifiers
|
||
|
//
|
||
|
|
||
|
void swap(poly& p) noexcept
|
||
|
{
|
||
|
using std::swap;
|
||
|
swap(ptr_, p.ptr_);
|
||
|
swap(cb_, p.cb_);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Observers
|
||
|
//
|
||
|
|
||
|
explicit operator bool() const
|
||
|
{
|
||
|
return (bool)cb_;
|
||
|
}
|
||
|
|
||
|
const T* operator->() const
|
||
|
{
|
||
|
assert(ptr_);
|
||
|
return ptr_;
|
||
|
}
|
||
|
|
||
|
const T& operator*() const
|
||
|
{
|
||
|
assert(*this);
|
||
|
return *ptr_;
|
||
|
}
|
||
|
|
||
|
T* operator->()
|
||
|
{
|
||
|
assert(*this);
|
||
|
return ptr_;
|
||
|
}
|
||
|
|
||
|
T& operator*()
|
||
|
{
|
||
|
assert(*this);
|
||
|
return *ptr_;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// poly creation
|
||
|
//
|
||
|
template <class T, class... Ts>
|
||
|
poly<T> make_poly(Ts&&... ts)
|
||
|
{
|
||
|
poly<T> p;
|
||
|
p.cb_ = std::make_unique<detail::direct_control_block<T, T>>(
|
||
|
std::forward<Ts>(ts)...);
|
||
|
p.ptr_ = p.cb_->ptr();
|
||
|
return std::move(p);
|
||
|
}
|
||
|
template <class T, class U, class... Ts>
|
||
|
poly<T> make_poly(Ts&&... ts)
|
||
|
{
|
||
|
poly<T> p;
|
||
|
p.cb_ = std::make_unique<detail::direct_control_block<T, U>>(
|
||
|
std::forward<Ts>(ts)...);
|
||
|
p.ptr_ = p.cb_->ptr();
|
||
|
return std::move(p);
|
||
|
}
|
||
|
|
||
|
template <class T, class U>
|
||
|
const T* poly_view(const poly<U>& p) {
|
||
|
if (p) {
|
||
|
return dynamic_cast<const T*>(&*p);
|
||
|
}
|
||
|
else {
|
||
|
return nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T, class U>
|
||
|
T* poly_view(poly<U>& p) {
|
||
|
if (p) {
|
||
|
return dynamic_cast<T*>(&*p);
|
||
|
}
|
||
|
else {
|
||
|
return nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T, class U>
|
||
|
poly<T> poly_cast(poly<U> p) {
|
||
|
poly<T> ret;
|
||
|
if (T* ptr = dynamic_cast<T*>(&*p)) {
|
||
|
ret.cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
|
||
|
std::move(p.cb_));
|
||
|
ret.ptr_ = ret.cb_->ptr();
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#endif // POLY_H
|