/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkUniquePtr_DEFINED #define SkUniquePtr_DEFINED #include "SkTLogic.h" #include <cstddef> #include <utility> namespace skstd { template <typename T> struct default_delete { /*constexpr*/ default_delete() /*noexcept*/ = default; template <typename U, typename = enable_if_t<is_convertible<U*, T*>::value>> default_delete(const default_delete<U>&) /*noexcept*/ {} void operator()(T* obj) const { static_assert(sizeof(T) > 0, "Deleting pointer to incomplete type!"); delete obj; } }; template <typename T> struct default_delete<T[]> { /*constexpr*/ default_delete() /*noexcept*/ = default; void operator()(T* obj) const { static_assert(sizeof(T) > 0, "Deleting pointer to incomplete type!"); delete [] obj; } }; template <typename T, typename D = default_delete<T>> class unique_ptr { // remove_reference_t<D>::pointer if that type exists, otherwise T*. struct pointer_type_detector { template <typename U> static typename U::pointer detector(typename U::pointer*); template <typename U> static T* detector(...); using type = decltype(detector<remove_reference_t<D>>(0)); }; public: using pointer = typename pointer_type_detector::type; using element_type = T; using deleter_type = D; private: template <typename B, bool> struct compressed_base : private B { /*constexpr*/ compressed_base() : B() {} /*constexpr*/ compressed_base(const B& b) : B(b) {} /*constexpr*/ compressed_base(B&& b) : B(std::move(b)) {} /*constexpr*/ B& get() /*noexcept*/ { return *this; } /*constexpr*/ B const& get() const /*noexcept*/ { return *this; } void swap(compressed_base&) /*noexcept*/ { } }; template <typename B> struct compressed_base<B, false> { B fb; /*constexpr*/ compressed_base() : B() {} /*constexpr*/ compressed_base(const B& b) : fb(b) {} /*constexpr*/ compressed_base(B&& b) : fb(std::move(b)) {} /*constexpr*/ B& get() /*noexcept*/ { return fb; } /*constexpr*/ B const& get() const /*noexcept*/ { return fb; } void swap(compressed_base& that) /*noexcept*/ { SkTSwap(fb, that.fB); } }; // C++14 adds '&& !std::is_final<deleter_type>::value' to the bool condition. // compressed_base_t exists and has this form to work around a bug in vs2013sp2-3 using compressed_base_t = compressed_base<deleter_type, std::is_empty<deleter_type>::value>; struct compressed_data : private compressed_base_t { pointer fPtr; /*constexpr*/ compressed_data() : compressed_base_t(), fPtr() {} /*constexpr*/ compressed_data(const pointer& ptr, const deleter_type& d) : compressed_base_t(d), fPtr(ptr) {} template <typename U1, typename U2, typename = enable_if_t< is_convertible<U1, pointer>::value && is_convertible<U2, deleter_type>::value >> /*constexpr*/ compressed_data(U1&& ptr, U2&& d) : compressed_base_t(std::forward<U2>(d)), fPtr(std::forward<U1>(ptr)) {} /*constexpr*/ pointer& getPointer() /*noexcept*/ { return fPtr; } /*constexpr*/ pointer const& getPointer() const /*noexcept*/ { return fPtr; } /*constexpr*/ deleter_type& getDeleter() /*noexcept*/ { return compressed_base_t::get(); } /*constexpr*/ deleter_type const& getDeleter() const /*noexcept*/ { return compressed_base_t::get(); } void swap(compressed_data& that) /*noexcept*/ { compressed_base_t::swap(static_cast<compressed_base_t>(that)); SkTSwap(fPtr, that.fPtr); } }; compressed_data data; public: /*constexpr*/ unique_ptr() /*noexcept*/ : data() { static_assert(!std::is_pointer<deleter_type>::value, "Deleter nullptr function pointer!"); } /*constexpr*/ unique_ptr(std::nullptr_t) /*noexcept*/ : unique_ptr() { } explicit unique_ptr(pointer ptr) /*noexcept*/ : data(ptr, deleter_type()) { static_assert(!std::is_pointer<deleter_type>::value, "Deleter nullptr function pointer!"); } unique_ptr(pointer ptr, conditional_t<std::is_reference<deleter_type>::value, deleter_type, const deleter_type&> d) /*noexcept*/ : data(ptr, d) {} unique_ptr(pointer ptr, remove_reference_t<deleter_type>&& d) /*noexcept*/ : data(std::move(ptr), std::move(d)) { static_assert(!std::is_reference<deleter_type>::value, "Binding an rvalue reference deleter as an lvalue reference deleter is not allowed."); } unique_ptr(unique_ptr&& that) /*noexcept*/ : data(that.release(), std::forward<deleter_type>(that.get_deleter())) {} template <typename U, typename ThatD, typename = enable_if_t< is_convertible<typename unique_ptr<U, ThatD>::pointer, pointer>::value && !std::is_array<U>::value && conditional_t<std::is_reference<D>::value, std::is_same<ThatD, D>, is_convertible<ThatD, D>>::value>> unique_ptr(unique_ptr<U, ThatD>&& that) /*noexcept*/ : data(that.release(), std::forward<ThatD>(that.get_deleter())) {} ~unique_ptr() /*noexcept*/ { pointer& ptr = data.getPointer(); if (ptr != nullptr) { get_deleter()(ptr); } ptr = pointer(); } unique_ptr& operator=(unique_ptr&& that) /*noexcept*/ { reset(that.release()); get_deleter() = std::forward<deleter_type>(that.get_deleter()); return *this; } template <typename U, typename ThatD> enable_if_t< is_convertible<typename unique_ptr<U, ThatD>::pointer, pointer>::value && !std::is_array<U>::value, unique_ptr&> operator=(unique_ptr<U, ThatD>&& that) /*noexcept*/ { reset(that.release()); get_deleter() = std::forward<ThatD>(that.get_deleter()); return *this; } unique_ptr& operator=(std::nullptr_t) /*noexcept*/ { reset(); return *this; } add_lvalue_reference_t<element_type> operator*() const { SkASSERT(get() != pointer()); return *get(); } pointer operator->() const /*noexcept*/ { SkASSERT(get() != pointer()); return get(); } pointer get() const /*noexcept*/ { return data.getPointer(); } deleter_type& get_deleter() /*noexcept*/ { return data.getDeleter(); } const deleter_type& get_deleter() const /*noexcept*/ { return data.getDeleter(); } //explicit operator bool() const noexcept { bool is_attached() const /*noexcept*/ { return get() == pointer() ? false : true; } pointer release() /*noexcept*/ { pointer ptr = get(); data.getPointer() = pointer(); return ptr; } void reset(pointer ptr = pointer()) /*noexcept*/ { SkTSwap(data.getPointer(), ptr); if (ptr != pointer()) { get_deleter()(ptr); } } void swap(unique_ptr& that) /*noexcept*/ { SkTSwap(data, that.data); } unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete; }; template <typename T, typename D> class unique_ptr<T[], D> { // remove_reference_t<D>::pointer if that type exists, otherwise T*. struct pointer_type_detector { template <typename U> static typename U::pointer detector(typename U::pointer*); template <typename U> static T* detector(...); using type = decltype(detector<remove_reference_t<D>>(0)); }; public: using pointer = typename pointer_type_detector::type; using element_type = T; using deleter_type = D; private: template <typename B, bool> struct compressed_base : private B { /*constexpr*/ compressed_base() : B() {} /*constexpr*/ compressed_base(const B& b) : B(b) {} /*constexpr*/ compressed_base(B&& b) : B(std::move(b)) {} /*constexpr*/ B& get() /*noexcept*/ { return *this; } /*constexpr*/ B const& get() const /*noexcept*/ { return *this; } void swap(compressed_base&) /*noexcept*/ { } }; template <typename B> struct compressed_base<B, false> { B fb; /*constexpr*/ compressed_base() : B() {} /*constexpr*/ compressed_base(const B& b) : fb(b) {} /*constexpr*/ compressed_base(B&& b) : fb(std::move(b)) {} /*constexpr*/ B& get() /*noexcept*/ { return fb; } /*constexpr*/ B const& get() const /*noexcept*/ { return fb; } void swap(compressed_base& that) /*noexcept*/ { SkTSwap(fb, that.fB); } }; // C++14 adds '&& !std::is_final<deleter_type>::value' to the bool condition. // compressed_base_t exists and has this form to work around a bug in vs2013sp2-3 using compressed_base_t = compressed_base<deleter_type, std::is_empty<deleter_type>::value>; struct compressed_data : private compressed_base_t { pointer fPtr; /*constexpr*/ compressed_data() : compressed_base_t(), fPtr() {} /*constexpr*/ compressed_data(const pointer& ptr, const deleter_type& d) : compressed_base_t(d), fPtr(ptr) {} template <typename U1, typename U2, typename = enable_if_t< is_convertible<U1, pointer>::value && is_convertible<U2, deleter_type>::value >> /*constexpr*/ compressed_data(U1&& ptr, U2&& d) : compressed_base_t(std::forward<U2>(d)), fPtr(std::forward<U1>(ptr)) {} /*constexpr*/ pointer& getPointer() /*noexcept*/ { return fPtr; } /*constexpr*/ pointer const& getPointer() const /*noexcept*/ { return fPtr; } /*constexpr*/ deleter_type& getDeleter() /*noexcept*/ { return compressed_base_t::get(); } /*constexpr*/ deleter_type const& getDeleter() const /*noexcept*/ { return compressed_base_t::get(); } void swap(compressed_data& that) /*noexcept*/ { compressed_base_t::swap(static_cast<compressed_base_t>(that)); SkTSwap(fPtr, that.fPtr); } }; compressed_data data; public: /*constexpr*/ unique_ptr() /*noexcept*/ : data() { static_assert(!std::is_pointer<deleter_type>::value, "Deleter nullptr function pointer!"); } /*constexpr*/ unique_ptr(std::nullptr_t) /*noexcept*/ : unique_ptr() { } explicit unique_ptr(pointer ptr) /*noexcept*/ : data(ptr, deleter_type()) { static_assert(!std::is_pointer<deleter_type>::value, "Deleter nullptr function pointer!"); } unique_ptr(pointer ptr, conditional_t<std::is_reference<deleter_type>::value, deleter_type, const deleter_type&> d) /*noexcept*/ : data(ptr, d) {} unique_ptr(pointer ptr, remove_reference_t<deleter_type>&& d) /*noexcept*/ : data(std::move(ptr), std::move(d)) { static_assert(!std::is_reference<deleter_type>::value, "Binding an rvalue reference deleter as an lvalue reference deleter is not allowed."); } unique_ptr(unique_ptr&& that) /*noexcept*/ : data(that.release(), std::forward<deleter_type>(that.get_deleter())) {} ~unique_ptr() { pointer& ptr = data.getPointer(); if (ptr != nullptr) { get_deleter()(ptr); } ptr = pointer(); } unique_ptr& operator=(unique_ptr&& that) /*noexcept*/ { reset(that.release()); get_deleter() = std::forward<deleter_type>(that.get_deleter()); return *this; } unique_ptr& operator=(std::nullptr_t) /*noexcept*/ { reset(); return *this; } add_lvalue_reference_t<element_type> operator[](size_t i) const { SkASSERT(get() != pointer()); return get()[i]; } pointer get() const /*noexcept*/ { return data.getPointer(); } deleter_type& get_deleter() /*noexcept*/ { return data.getDeleter(); } const deleter_type& get_deleter() const /*noexcept*/ { return data.getDeleter(); } //explicit operator bool() const noexcept { bool is_attached() const /*noexcept*/ { return get() == pointer() ? false : true; } pointer release() /*noexcept*/ { pointer ptr = get(); data.getPointer() = pointer(); return ptr; } void reset(pointer ptr = pointer()) /*noexcept*/ { SkTSwap(data.getPointer(), ptr); if (ptr != pointer()) { get_deleter()(ptr); } } template <typename U> void reset(U*) = delete; void swap(unique_ptr& that) /*noexcept*/ { data.swap(that.data); } unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete; }; template <typename T, typename D> inline void swap(unique_ptr<T, D>& a, unique_ptr<T, D>& b) /*noexcept*/ { a.swap(b); } template <typename T, typename D, typename U, typename ThatD> inline bool operator==(const unique_ptr<T, D>& a, const unique_ptr<U, ThatD>& b) { return a.get() == b.get(); } template <typename T, typename D> inline bool operator==(const unique_ptr<T, D>& a, std::nullptr_t) /*noexcept*/ { //return !a; return !a.is_attached(); } template <typename T, typename D> inline bool operator==(std::nullptr_t, const unique_ptr<T, D>& b) /*noexcept*/ { //return !b; return !b.is_attached(); } template <typename T, typename D, typename U, typename ThatD> inline bool operator!=(const unique_ptr<T, D>& a, const unique_ptr<U, ThatD>& b) { return a.get() != b.get(); } template <typename T, typename D> inline bool operator!=(const unique_ptr<T, D>& a, std::nullptr_t) /*noexcept*/ { //return (bool)a; return a.is_attached(); } template <typename T, typename D> inline bool operator!=(std::nullptr_t, const unique_ptr<T, D>& b) /*noexcept*/ { //return (bool)b; return b.is_attached(); } } // namespace skstd #endif