#pragma once #ifndef MESSMER_CPP_UTILS_POINTER_UNIQUE_REF_H #define MESSMER_CPP_UTILS_POINTER_UNIQUE_REF_H #include #include #include "../macros.h" #include "cast.h" namespace cpputils { /** * unique_ref behaves like unique_ptr, but guarantees that the pointer points to a valid object. * You can create objects using make_unique_ref (works like make_unique for unique_ptr). * * If you happen to already have a unique_ptr, you can call nullcheck(unique_ptr), * which returns optional>. * Take care that this should be used very rarely, since it circumvents parts of the guarantee. * It still protects against null pointers, but it does not guarantee anymore that the pointer points * to a valid object. It might hold an arbitrary non-null memory location. * * Caution: There is one way a unique_ref can actually hold a nullptr. * It will hold a nullptr after its value was moved to another unique_ref. * Never use the old instance after moving! */ template class unique_ref final { public: unique_ref(unique_ref&& from): _target(std::move(from._target)) {} // TODO Test this upcast-allowing move constructor template unique_ref(unique_ref&& from): _target(std::move(from._target)) {} unique_ref& operator=(unique_ref&& from) { _target = std::move(from._target); return *this; } // TODO Test this upcast-allowing assignment template unique_ref& operator=(unique_ref&& from) { _target = std::move(from._target); return *this; } typename std::add_lvalue_reference::type operator*() const& { return *_target; } typename std::add_rvalue_reference::type operator*() && { return std::move(*_target); } T* operator->() const { return get(); } T* get() const { return _target.get(); } void swap(unique_ref& rhs) { std::swap(_target, rhs._target); } private: unique_ref(std::unique_ptr target): _target(std::move(target)) {} template friend unique_ref make_unique_ref(Args&&... args); template friend boost::optional> nullcheck(std::unique_ptr ptr); template friend class unique_ref; template friend boost::optional> dynamic_pointer_move(unique_ref &source); template friend std::unique_ptr to_unique_ptr(unique_ref ref); std::unique_ptr _target; DISALLOW_COPY_AND_ASSIGN(unique_ref); }; template inline unique_ref make_unique_ref(Args&&... args) { return unique_ref(std::make_unique(std::forward(args)...)); } template inline boost::optional> nullcheck(std::unique_ptr ptr) { if (ptr.get() != nullptr) { return unique_ref(std::move(ptr)); } return boost::none; } template inline void destruct(unique_ref ptr) { to_unique_ptr(std::move(ptr)).reset(); } //TODO Also allow passing a rvalue reference, otherwise dynamic_pointer_move(func()) won't work template inline boost::optional> dynamic_pointer_move(unique_ref &source) { return nullcheck(dynamic_pointer_move(source._target)); } //TODO Write test cases for to_unique_ptr template inline std::unique_ptr to_unique_ptr(unique_ref ref) { return std::move(ref._target); } template inline bool operator==(const unique_ref &lhs, const unique_ref &rhs) { return lhs.get() == rhs.get(); } template inline bool operator!=(const unique_ref &lhs, const unique_ref &rhs) { return !operator==(lhs, rhs); } } namespace std { template inline void swap(cpputils::unique_ref& lhs, cpputils::unique_ref& rhs) { lhs.swap(rhs); } template inline void swap(cpputils::unique_ref&& lhs, cpputils::unique_ref& rhs) { lhs.swap(rhs); } template inline void swap(cpputils::unique_ref& lhs, cpputils::unique_ref&& rhs) { lhs.swap(rhs); } // Allow using it in std::unordered_set / std::unordered_map template struct hash> { size_t operator()(const cpputils::unique_ref &ref) const { return (size_t)ref.get(); } }; // Allow using it in std::map / std::set template struct less> { bool operator()(const cpputils::unique_ref &lhs, const cpputils::unique_ref &rhs) const { return lhs.get() < rhs.get(); } }; } #endif