#pragma once #if !defined(MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED) #define MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED 1 #include #include #include "../debug/assert.hpp" namespace mijin { // // public defines // #if !defined(MIJIN_BOXED_OBJECT_DEBUG) #if defined(MIJIN_DEBUG) #define MIJIN_BOXED_OBJECT_DEBUG MIJIN_DEBUG #else #define MIJIN_BOXED_OBJECT_DEBUG 0 #endif #endif // !defined(MIJIN_BOXED_OBJECT_DEBUG) #define MIJIN_BOXED_PROXY_FUNC(funcname) \ template \ decltype(auto) funcname(TFuncArgs&&... args) \ { \ return base_t::get().funcname(std::forward(args)...); \ } #define MIJIN_BOXED_PROXY_FUNC_CONST(funcname) \ template \ decltype(auto) funcname(TFuncArgs&&... args) const \ { \ return base_t::get().funcname(std::forward(args)...); \ } // // public constants // // // public types // template class BoxedObject { private: union { std::byte placeholder_; T object_; }; #if MIJIN_BOXED_OBJECT_DEBUG bool constructed = false; #endif public: constexpr BoxedObject() MIJIN_NOEXCEPT : placeholder_() {}; explicit constexpr BoxedObject(T object) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) #if MIJIN_BOXED_OBJECT_DEBUG : constructed(true) #endif { std::construct_at(&object_, std::move(object)); } BoxedObject(const BoxedObject&) = delete; BoxedObject(BoxedObject&&) = delete; #if MIJIN_BOXED_OBJECT_DEBUG constexpr ~BoxedObject() noexcept { MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroyed prior to destructor!"); } #endif BoxedObject& operator=(const BoxedObject&) = delete; BoxedObject& operator=(BoxedObject&&) = delete; constexpr T& operator*() noexcept { return get(); } constexpr const T& operator*() const noexcept { return get(); } constexpr T* operator->() noexcept { return &get(); } constexpr const T* operator->() const noexcept { return &get(); } template constexpr void construct(TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v)) { #if MIJIN_BOXED_OBJECT_DEBUG MIJIN_ASSERT(!constructed, "BoxedObject::construct(): Attempt to construct an already constructed object!"); constructed = true; #endif std::construct_at(&object_, std::forward(args)...); } constexpr void destroy() MIJIN_NOEXCEPT { #if MIJIN_BOXED_OBJECT_DEBUG MIJIN_ASSERT(constructed, "BoxedObject::destroy(): Attempt to destroy a not constructed object!"); constructed = false; #endif std::destroy_at(&object_); } constexpr void copyTo(BoxedObject& other) const MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) { #if MIJIN_BOXED_OBJECT_DEBUG MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!"); #endif other.construct(object_); } constexpr void moveTo(BoxedObject& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) { #if MIJIN_BOXED_OBJECT_DEBUG MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!"); #endif other.construct(std::move(object_)); destroy(); } [[nodiscard]] constexpr T& get() MIJIN_NOEXCEPT { #if MIJIN_BOXED_OBJECT_DEBUG MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!"); #endif return object_; } [[nodiscard]] constexpr const T& get() const MIJIN_NOEXCEPT { #if MIJIN_BOXED_OBJECT_DEBUG MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!"); #endif return object_; } }; template class BoxedObjectIterator { public: using difference_type = std::ptrdiff_t; using value_type = T; private: BoxedObject* box_ = nullptr; #if MIJIN_CHECKED_ITERATORS BoxedObject* start_ = nullptr; BoxedObject* end_ = nullptr; #endif public: BoxedObjectIterator() noexcept = default; explicit constexpr BoxedObjectIterator(BoxedObject* box, BoxedObject* start, BoxedObject* end) MIJIN_NOEXCEPT : box_(box) #if MIJIN_CHECKED_ITERATORS , start_(start), end_(end) #endif {} constexpr BoxedObjectIterator(const BoxedObjectIterator&) noexcept = default; constexpr BoxedObjectIterator(BoxedObjectIterator&&) noexcept = default; constexpr BoxedObjectIterator& operator=(const BoxedObjectIterator&) noexcept = default; constexpr BoxedObjectIterator& operator=(BoxedObjectIterator&&) noexcept = default; constexpr BoxedObjectIterator& operator+=(difference_type diff) MIJIN_NOEXCEPT; constexpr BoxedObjectIterator& operator-=(difference_type diff) MIJIN_NOEXCEPT { return (*this += -diff); } constexpr auto operator<=>(const BoxedObjectIterator& other) const noexcept = default; [[nodiscard]] constexpr T& operator*() const MIJIN_NOEXCEPT; [[nodiscard]] constexpr T* operator->() const MIJIN_NOEXCEPT; constexpr BoxedObjectIterator& operator++() MIJIN_NOEXCEPT; constexpr BoxedObjectIterator operator++(int) MIJIN_NOEXCEPT; constexpr BoxedObjectIterator& operator--() MIJIN_NOEXCEPT; constexpr BoxedObjectIterator operator--(int) MIJIN_NOEXCEPT; [[nodiscard]] constexpr difference_type operator-(const BoxedObjectIterator& other) const MIJIN_NOEXCEPT { return box_ - other.box_; } [[nodiscard]] constexpr BoxedObjectIterator operator+(difference_type diff) const MIJIN_NOEXCEPT; [[nodiscard]] constexpr BoxedObjectIterator operator-(difference_type diff) const MIJIN_NOEXCEPT { return (*this + -diff); } [[nodiscard]] T& operator[](difference_type diff) const MIJIN_NOEXCEPT { return *(*this + diff); } }; template constexpr BoxedObjectIterator operator+(std::iter_difference_t diff, const BoxedObjectIterator& iter) MIJIN_NOEXCEPT { return iter + diff; } static_assert(std::random_access_iterator>); // // public functions // template constexpr BoxedObjectIterator& BoxedObjectIterator::operator+=(difference_type diff) MIJIN_NOEXCEPT { #if MIJIN_CHECKED_ITERATORS MIJIN_ASSERT(diff <= (end_ - box_) && diff >= (box_ - start_), "BoxedObjectIterator::operator+=(): Attempt to iterate out of range."); #endif box_ += diff; return *this; } template constexpr T& BoxedObjectIterator::operator*() const MIJIN_NOEXCEPT { #if MIJIN_CHECKED_ITERATORS MIJIN_ASSERT(box_ >= start_ && box_ < end_, "BoxedObjectIterator::operator->(): Attempt to dereference out-of-range iterator."); #endif return box_->get(); } template constexpr BoxedObjectIterator& BoxedObjectIterator::operator++() MIJIN_NOEXCEPT { #if MIJIN_CHECKED_ITERATORS MIJIN_ASSERT(box_ < end_, "BoxedObjectIterator::operator++(): Attempt to iterator past the end."); #endif ++box_; } template constexpr BoxedObjectIterator BoxedObjectIterator::operator++(int) MIJIN_NOEXCEPT { #if MIJIN_CHECKED_ITERATORS MIJIN_ASSERT(box_ < end_, "BoxedObjectIterator::operator++(int): Attempt to iterator past the end."); #endif BoxedObjectIterator copy(*this); ++box_; return copy; } template constexpr BoxedObjectIterator& BoxedObjectIterator::operator--() MIJIN_NOEXCEPT { #if MIJIN_CHECKED_ITERATORS MIJIN_ASSERT(box_ > start_, "BoxedObjectIterator::operator--(): Attempt to iterator past start."); #endif --box_; } template constexpr BoxedObjectIterator BoxedObjectIterator::operator--(int) MIJIN_NOEXCEPT { #if MIJIN_CHECKED_ITERATORS MIJIN_ASSERT(box_ > start_, "BoxedObjectIterator::operator--(int): Attempt to iterator past start."); #endif BoxedObjectIterator copy(*this); --box_; return copy; } template constexpr BoxedObjectIterator BoxedObjectIterator::operator+(difference_type diff) const MIJIN_NOEXCEPT { BoxedObjectIterator copy(*this); copy += diff; return copy; } } // namespace mijin #endif // !defined(MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED)