#ifndef RFL_BOX_HPP_ #define RFL_BOX_HPP_ #include #include #include "Result.hpp" namespace rfl { /// The Box class behaves very similarly to the unique_ptr, but unlike the /// unique_ptr, it is 100% guaranteed to be filled at all times (unless the user /// tries to access it after calling std::move does something else that is /// clearly bad practice). template class Box { public: /// The only way of creating new boxes is /// Box::make(...). template static Box make(Args&&... _args) { return Box(std::make_unique(std::forward(_args)...)); } /// You can generate them from unique_ptrs as well, in which case it will /// return an Error, if the unique_ptr is not set. static Result> make(std::unique_ptr&& _ptr) { if (!_ptr) { return Error("std::unique_ptr was a nullptr."); } return Box(std::move(_ptr)); } Box() : ptr_(std::make_unique()) {} Box(const Box& _other) = delete; Box(Box&& _other) = default; template Box(Box&& _other) noexcept : ptr_(std::forward>(_other.ptr())) {} ~Box() = default; /// Returns a pointer to the underlying object T* get() const { return ptr_.get(); } /// Copy assignment operator Box& operator=(const Box& _other) = delete; /// Move assignment operator Box& operator=(Box&& _other) noexcept = default; /// Move assignment operator template Box& operator=(Box&& _other) noexcept { ptr_ = std::forward>(_other.ptr()); return *this; } /// Returns the underlying object. T& operator*() { return *ptr_; } /// Returns the underlying object. T& operator*() const { return *ptr_; } /// Returns the underlying object. T* operator->() { return ptr_.get(); } /// Returns the underlying object. T* operator->() const { return ptr_.get(); } /// Returns the underlying unique_ptr std::unique_ptr& ptr() { return ptr_; } /// Returns the underlying unique_ptr const std::unique_ptr& ptr() const { return ptr_; } private: /// Only make is allowed to use this constructor. explicit Box(std::unique_ptr&& _ptr) : ptr_(std::move(_ptr)) {} private: /// The underlying unique_ptr_ std::unique_ptr ptr_; }; /// Generates a new Ref. template auto make_box(Args&&... _args) { return Box::make(std::forward(_args)...); } template inline auto operator<=>(const Box& _b1, const Box& _b2) { return _b1.ptr() <=> _b2.ptr(); } template inline std::basic_ostream& operator<<( std::basic_ostream& _os, const Box& _b) { _os << _b.get(); return _os; } } // namespace rfl namespace std { template struct hash> { size_t operator()(const rfl::Box& _b) const { return hash>()(_b.ptr()); } }; template inline void swap(rfl::Box& _b1, rfl::Box& _b2) { return swap(_b1.ptr(), _b2.ptr()); } } // namespace std #endif