#ifndef RFL_BOX_HPP_ #define RFL_BOX_HPP_ #include #include "Result.hpp" namespace rfl { enum class Copyability { COPYABLE, NON_COPYABLE }; /// 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). /// /// By default Box behaves like a unique_ptr in relation to copying, but it can be /// configured to add copy constructor and assignment operators that call the /// same function of the contained type T. 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()) {} /// Copy constructor if copyable Box(const Box& _other) requires (C == Copyability::COPYABLE) { ptr_ = std::make_unique(*_other); } /// Copy constructor if not copyable Box(const Box& _other) requires (C == Copyability::NON_COPYABLE) = 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 if copyable Box& operator=(const Box& other) requires (C == Copyability::COPYABLE) { if(this != &other) { ptr_ = std::make_unique(*other); } return *this; } /// Copy assignment operator if not copyable Box& operator=(const Box& _other) requires (C == Copyability::NON_COPYABLE) = 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 specialization for a box that is copyable. template using CopyableBox = Box; template auto make_copyable_box(Args&&... _args) { return CopyableBox::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 std::hash>{}(_b.ptr()); } }; template inline void swap(rfl::Box& _b1, rfl::Box& _b2) { return swap(_b1.ptr(), _b2.ptr()); } } // namespace std #endif