///|/ Copyright (c) Prusa Research 2023 Tomáš Mészáros @tamasmeszaros ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ #ifndef ANYPTR_HPP #define ANYPTR_HPP #include #include #include namespace Slic3r { // A general purpose pointer holder that can hold any type of smart pointer // or raw pointer which can own or not own any object they point to. // In case a raw pointer is stored, it is not destructed so ownership is // assumed to be foreign. // // The stored pointer is not checked for being null when dereferenced. // // This is a movable only object due to the fact that it can possibly hold // a unique_ptr which can only be moved. // // Drawbacks: // No custom deleters are supported when storing a unique_ptr, but overloading // std::default_delete for a particular type could be a workaround // // raw array types are problematic, since std::default_delete also does not // support them well. template class AnyPtr { enum { RawPtr, UPtr, ShPtr }; boost::variant, std::shared_ptr> ptr; template static T *get_ptr(Self &&s) { switch (s.ptr.which()) { case RawPtr: return boost::get(s.ptr); case UPtr: return boost::get>(s.ptr).get(); case ShPtr: return boost::get>(s.ptr).get(); } return nullptr; } template friend class AnyPtr; template using SimilarPtrOnly = std::enable_if_t>; public: AnyPtr() noexcept = default; AnyPtr(T *p) noexcept: ptr{p} {} AnyPtr(std::nullptr_t) noexcept {}; template> AnyPtr(TT *p) noexcept : ptr{p} {} template> AnyPtr(std::unique_ptr p) noexcept : ptr{std::unique_ptr(std::move(p))} {} template> AnyPtr(std::shared_ptr p) noexcept : ptr{std::shared_ptr(std::move(p))} {} AnyPtr(AnyPtr &&other) noexcept : ptr{std::move(other.ptr)} {} template> AnyPtr(AnyPtr &&other) noexcept { this->operator=(std::move(other)); } AnyPtr(const AnyPtr &other) = delete; AnyPtr &operator=(AnyPtr &&other) noexcept { ptr = std::move(other.ptr); return *this; } AnyPtr &operator=(const AnyPtr &other) = delete; template> AnyPtr& operator=(AnyPtr &&other) noexcept { switch (other.ptr.which()) { case RawPtr: *this = boost::get(other.ptr); break; case UPtr: *this = std::move(boost::get>(other.ptr)); break; case ShPtr: *this = std::move(boost::get>(other.ptr)); break; } return *this; } template> AnyPtr &operator=(TT *p) noexcept { ptr = static_cast(p); return *this; } template> AnyPtr &operator=(std::unique_ptr p) noexcept { ptr = std::unique_ptr(std::move(p)); return *this; } template> AnyPtr &operator=(std::shared_ptr p) noexcept { ptr = std::shared_ptr(std::move(p)); return *this; } const T &operator*() const noexcept { return *get_ptr(*this); } T &operator*() noexcept { return *get_ptr(*this); } T *operator->() noexcept { return get_ptr(*this); } const T *operator->() const noexcept { return get_ptr(*this); } T *get() noexcept { return get_ptr(*this); } const T *get() const noexcept { return get_ptr(*this); } operator bool() const noexcept { switch (ptr.which()) { case RawPtr: return bool(boost::get(ptr)); case UPtr: return bool(boost::get>(ptr)); case ShPtr: return bool(boost::get>(ptr)); } return false; } // If the stored pointer is a shared pointer, returns a reference // counted copy. Empty shared pointer is returned otherwise. std::shared_ptr get_shared_cpy() const noexcept { std::shared_ptr ret; if (ptr.which() == ShPtr) ret = boost::get>(ptr); return ret; } // If the underlying pointer is unique, convert to shared pointer void convert_unique_to_shared() noexcept { if (ptr.which() == UPtr) ptr = std::shared_ptr{std::move(boost::get>(ptr))}; } // Returns true if the data is owned by this AnyPtr instance bool is_owned() const noexcept { return ptr.which() == UPtr || ptr.which() == ShPtr; } }; } // namespace Slic3r #endif // ANYPTR_HPP