/* ==================================================================== A Smart Pointer to IMPLementation (i.e. Smart PIMPL or just SPIMPL). ==================================================================== Version: 1.1 Latest version: https://github.com/oliora/samples/blob/master/spimpl.h Rationale and description: http://oliora.github.io/2015/12/29/pimpl-and-rule-of-zero.html Copyright (c) 2015 Andrey Upadyshev (oliora@gmail.com) Distributed under the Boost Software License, Version 1.0. See http://www.boost.org/LICENSE_1_0.txt Changes history --------------- v1.1: - auto_ptr support is disabled by default for C++17 compatibility v1.0: - Released */ #ifndef SPIMPL_H_ #define SPIMPL_H_ #include #include #include #if defined _MSC_VER && _MSC_VER < 1900 // MS Visual Studio before VS2015 #define SPIMPL_NO_CPP11_NOEXCEPT #define SPIMPL_NO_CPP11_CONSTEXPR #define SPIMPL_NO_CPP11_DEFAULT_MOVE_SPEC_FUNC #endif #if !defined SPIMPL_NO_CPP11_NOEXCEPT #define SPIMPL_NOEXCEPT noexcept #else #define SPIMPL_NOEXCEPT #endif #if !defined SPIMPL_NO_CPP11_CONSTEXPR #define SPIMPL_CONSTEXPR constexpr #else #define SPIMPL_CONSTEXPR #endif // define SPIMPL_HAS_AUTO_PTR to enable constructor and assignment operator that accept // std::auto_ptr // TODO: auto detect std::auto_ptr support namespace spimpl { namespace details { template T *default_copy(T *src) { static_assert(sizeof(T) > 0, "default_copy cannot copy incomplete type"); static_assert(!std::is_void::value, "default_copy cannot copy incomplete type"); return new T(*src); } template void default_delete(T *p) SPIMPL_NOEXCEPT { static_assert(sizeof(T) > 0, "default_delete cannot delete incomplete type"); static_assert(!std::is_void::value, "default_delete cannot delete incomplete type"); delete p; } template struct default_deleter { using type = void (*)(T *); }; template using default_deleter_t = typename default_deleter::type; template struct default_copier { using type = T *(*)(T *); }; template using default_copier_t = typename default_copier::type; template struct is_default_manageable : public std::integral_constant >::value && std::is_same >::value> { }; } template , class Copier = details::default_copier_t > class impl_ptr { private: static_assert(!std::is_array::value, "impl_ptr specialization for arrays is not implemented"); struct dummy_t_ { int dummy__; }; public: using pointer = T *; using element_type = T; using copier_type = typename std::decay::type; using deleter_type = typename std::decay::type; using unique_ptr_type = std::unique_ptr; using is_default_manageable = details::is_default_manageable; SPIMPL_CONSTEXPR impl_ptr() SPIMPL_NOEXCEPT : ptr_(nullptr, deleter_type{}), copier_(copier_type{}) { } SPIMPL_CONSTEXPR impl_ptr(std::nullptr_t) SPIMPL_NOEXCEPT : impl_ptr() {} template impl_ptr(pointer p, D &&d, C &&c, typename std::enable_if::value && std::is_convertible::value, dummy_t_>::type = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(std::move(p), std::forward(d)), copier_(std::forward(c)) { } template impl_ptr(U *u, typename std::enable_if::value && is_default_manageable::value, dummy_t_>::type = dummy_t_()) SPIMPL_NOEXCEPT : impl_ptr(u, &details::default_delete, &details::default_copy) { } impl_ptr(const impl_ptr &r) : impl_ptr(r.clone()) {} #ifndef SPIMPL_NO_CPP11_DEFAULT_MOVE_SPEC_FUNC impl_ptr(impl_ptr &&r) SPIMPL_NOEXCEPT = default; #else impl_ptr(impl_ptr &&r) SPIMPL_NOEXCEPT : ptr_(std::move(r.ptr_)), copier_(std::move(r.copier_)) { } #endif #ifdef SPIMPL_HAS_AUTO_PTR template impl_ptr(std::auto_ptr &&u, typename std::enable_if::value && is_default_manageable::value, dummy_t_>::type = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(u.release(), &details::default_delete), copier_(&details::default_copy) { } #endif template impl_ptr(std::unique_ptr &&u, typename std::enable_if::value && is_default_manageable::value, dummy_t_>::type = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(u.release(), &details::default_delete), copier_(&details::default_copy) { } template impl_ptr(std::unique_ptr &&u, C &&c, typename std::enable_if::value && std::is_convertible::value && std::is_convertible::value, dummy_t_>::type = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(std::move(u)), copier_(std::forward(c)) { } template impl_ptr(impl_ptr &&u, typename std::enable_if::value && std::is_convertible::value && std::is_convertible::value, dummy_t_>::type = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(std::move(u.ptr_)), copier_(std::move(u.copier_)) { } impl_ptr &operator=(const impl_ptr &r) { if (this == &r) return *this; return operator=(r.clone()); } #ifndef SPIMPL_NO_CPP11_DEFAULT_MOVE_SPEC_FUNC impl_ptr &operator=(impl_ptr &&r) SPIMPL_NOEXCEPT = default; #else impl_ptr &operator=(impl_ptr &&r) SPIMPL_NOEXCEPT { ptr_ = std::move(r.ptr_); copier_ = std::move(r.copier_); return *this; } #endif template typename std::enable_if::value && std::is_convertible::value && std::is_convertible::value, impl_ptr &>::type operator=(impl_ptr &&u) SPIMPL_NOEXCEPT { ptr_ = std::move(u.ptr_); copier_ = std::move(u.copier_); return *this; } template typename std::enable_if::value && std::is_convertible::value && std::is_convertible::value, impl_ptr &>::type operator=(const impl_ptr &u) { return operator=(u.clone()); } // #ifdef SPIMPL_HAS_AUTO_PTR template typename std::enable_if::value && is_default_manageable::value, impl_ptr &>::type operator=(std::auto_ptr &&u) SPIMPL_NOEXCEPT { return operator=(impl_ptr(std::move(u))); } #endif template typename std::enable_if::value && is_default_manageable::value, impl_ptr &>::type operator=(std::unique_ptr &&u) SPIMPL_NOEXCEPT { return operator=(impl_ptr(std::move(u))); } impl_ptr clone() const { return impl_ptr(ptr_ ? copier_(ptr_.get()) : nullptr, ptr_.get_deleter(), copier_); } typename std::remove_reference::type &operator*() const { return *ptr_; } pointer operator->() const SPIMPL_NOEXCEPT { return get(); } pointer get() const SPIMPL_NOEXCEPT { return ptr_.get(); } void swap(impl_ptr &u) SPIMPL_NOEXCEPT { using std::swap; ptr_.swap(u.ptr_); swap(copier_, u.copier_); } pointer release() SPIMPL_NOEXCEPT { return ptr_.release(); } unique_ptr_type release_unique() SPIMPL_NOEXCEPT { return std::move(ptr_); } explicit operator bool() const SPIMPL_NOEXCEPT { return static_cast(ptr_); } typename std::remove_reference::type &get_deleter() SPIMPL_NOEXCEPT { return ptr_.get_deleter(); } const typename std::remove_reference::type &get_deleter() const SPIMPL_NOEXCEPT { return ptr_.get_deleter(); } typename std::remove_reference::type &get_copier() SPIMPL_NOEXCEPT { return copier_; } const typename std::remove_reference::type &get_copier() const SPIMPL_NOEXCEPT { return copier_; } private: unique_ptr_type ptr_; copier_type copier_; }; template inline void swap(impl_ptr &l, impl_ptr &r) SPIMPL_NOEXCEPT { l.swap(r); } template inline bool operator==(const impl_ptr &l, const impl_ptr &r) { return l.get() == r.get(); } template inline bool operator!=(const impl_ptr &l, const impl_ptr &r) { return !(l == r); } template inline bool operator<(const impl_ptr &l, const impl_ptr &r) { using P1 = typename impl_ptr::pointer; using P2 = typename impl_ptr::pointer; using CT = typename std::common_type::type; return std::less()(l.get(), r.get()); } template inline bool operator>(const impl_ptr &l, const impl_ptr &r) { return r < l; } template inline bool operator<=(const impl_ptr &l, const impl_ptr &r) { return !(r < l); } template inline bool operator>=(const impl_ptr &l, const impl_ptr &r) { return !(l < r); } template inline bool operator==(const impl_ptr &p, std::nullptr_t) SPIMPL_NOEXCEPT { return !p; } template inline bool operator==(std::nullptr_t, const impl_ptr &p) SPIMPL_NOEXCEPT { return !p; } template inline bool operator!=(const impl_ptr &p, std::nullptr_t) SPIMPL_NOEXCEPT { return static_cast(p); } template inline bool operator!=(std::nullptr_t, const impl_ptr &p) SPIMPL_NOEXCEPT { return static_cast(p); } template inline bool operator<(const impl_ptr &l, std::nullptr_t) { using P = typename impl_ptr::pointer; return std::less

()(l.get(), nullptr); } template inline bool operator<(std::nullptr_t, const impl_ptr &p) { using P = typename impl_ptr::pointer; return std::less

()(nullptr, p.get()); } template inline bool operator>(const impl_ptr &p, std::nullptr_t) { return nullptr < p; } template inline bool operator>(std::nullptr_t, const impl_ptr &p) { return p < nullptr; } template inline bool operator<=(const impl_ptr &p, std::nullptr_t) { return !(nullptr < p); } template inline bool operator<=(std::nullptr_t, const impl_ptr &p) { return !(p < nullptr); } template inline bool operator>=(const impl_ptr &p, std::nullptr_t) { return !(p < nullptr); } template inline bool operator>=(std::nullptr_t, const impl_ptr &p) { return !(nullptr < p); } template inline impl_ptr make_impl(Args &&... args) { return impl_ptr(new T(std::forward(args)...), &details::default_delete, &details::default_copy); } // Helpers to manage unique impl, stored in std::unique_ptr template using unique_impl_ptr = std::unique_ptr; template inline unique_impl_ptr make_unique_impl(Args &&... args) { static_assert(!std::is_array::value, "unique_impl_ptr does not support arrays"); return unique_impl_ptr(new T(std::forward(args)...), &details::default_delete); } } namespace std { template struct hash > { using argument_type = spimpl::impl_ptr; using result_type = size_t; result_type operator()(const argument_type &p) const SPIMPL_NOEXCEPT { return hash()(p.get()); } }; } #endif // SPIMPL_H_