#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_STORAGE_HPP #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_STORAGE_HPP #include #include "traits.hpp" namespace kgr { namespace detail { template using lazy_stored_type = typename std::conditional< std::is_lvalue_reference::value, typename std::add_pointer::type>::type, typename std::decay::type >::type; template class... Bases> struct LazyCrtpHelper : Bases... {}; template struct LazyCopyConstruct { LazyCopyConstruct() = default; LazyCopyConstruct(const LazyCopyConstruct&) = delete; LazyCopyConstruct(LazyCopyConstruct&&) = default; LazyCopyConstruct& operator=(const LazyCopyConstruct&) = default; LazyCopyConstruct& operator=(LazyCopyConstruct&&) = default; }; template struct LazyMoveAssign { LazyMoveAssign() = default; LazyMoveAssign(const LazyMoveAssign&) = default; LazyMoveAssign(LazyMoveAssign&&) = default; LazyMoveAssign& operator=(const LazyMoveAssign&) = default; LazyMoveAssign& operator=(LazyMoveAssign&&) = delete; }; template struct LazyCopyAssign { LazyCopyAssign() = default; LazyCopyAssign(const LazyCopyAssign&) = default; LazyCopyAssign(LazyCopyAssign&&) = default; LazyCopyAssign& operator=(const LazyCopyAssign&) = delete; LazyCopyAssign& operator=(LazyCopyAssign&&) = default; }; template struct LazyMoveConstruct { LazyMoveConstruct() = default; LazyMoveConstruct(const LazyMoveConstruct&) = default; LazyMoveConstruct(LazyMoveConstruct&&) = delete; LazyMoveConstruct& operator=(const LazyMoveConstruct&) = default; LazyMoveConstruct& operator=(LazyMoveConstruct&&) = default; }; template struct LazyCopyConstruct::value && !std::is_trivially_copy_constructible::value>> { LazyCopyConstruct(const LazyCopyConstruct& other) noexcept(std::is_nothrow_copy_constructible::value){ auto&& o = static_cast(other); if (o) { static_cast(*this).emplace(o.data()); } } LazyCopyConstruct() = default; LazyCopyConstruct& operator=(const LazyCopyConstruct&) = default; LazyCopyConstruct(LazyCopyConstruct&&) = default; LazyCopyConstruct& operator=(LazyCopyConstruct&&) = default; }; template struct LazyCopyConstruct::value>> {}; template struct LazyCopyAssign::value && std::is_copy_constructible::value && !(std::is_trivially_copy_assignable::value && std::is_trivially_copy_constructible::value && std::is_trivially_destructible::value) >> { LazyCopyAssign& operator=(const LazyCopyAssign& other) noexcept( std::is_nothrow_copy_assignable::value && std::is_nothrow_copy_constructible::value && std::is_nothrow_destructible::value ) { auto&& o = static_cast(other); auto&& self = static_cast(*this); if (o) { self.assign(o.data()); } else { self.destroy(); } return *this; } LazyCopyAssign() = default; LazyCopyAssign(const LazyCopyAssign&) = default; LazyCopyAssign(LazyCopyAssign&&) = default; LazyCopyAssign& operator=(LazyCopyAssign&&) = default; }; template struct LazyCopyAssign::value && std::is_trivially_copy_constructible::value && std::is_trivially_destructible::value >> {}; template struct LazyMoveConstruct::value && !std::is_trivially_move_constructible::value>> { LazyMoveConstruct(LazyMoveConstruct&& other) noexcept(std::is_nothrow_move_constructible::value) { auto&& o = static_cast(other); if (o) { static_cast(*this).emplace(std::move(o.data())); } } LazyMoveConstruct() = default; LazyMoveConstruct(const LazyMoveConstruct&) = default; LazyMoveConstruct& operator=(LazyMoveConstruct&&) = default; LazyMoveConstruct& operator=(const LazyMoveConstruct&) = default; }; template struct LazyMoveConstruct::value>> {}; template struct LazyMoveAssign::value && std::is_move_constructible::value && !(std::is_trivially_move_assignable::value && std::is_trivially_move_constructible::value && std::is_trivially_destructible::value) >> { LazyMoveAssign& operator=(LazyMoveAssign&& other) noexcept( std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value && std::is_nothrow_destructible::value ) { auto&& o = static_cast(other); auto&& self = static_cast(*this); if (o) { self.assign(std::move(o.data())); } else { self.destroy(); } return *this; } LazyMoveAssign() = default; LazyMoveAssign(const LazyMoveAssign&) = default; LazyMoveAssign(LazyMoveAssign&&) = default; LazyMoveAssign& operator=(const LazyMoveAssign&) = default; }; template struct LazyMoveAssign::value && std::is_trivially_move_constructible::value && std::is_trivially_destructible::value >> {}; template struct LazyDestruction { ~LazyDestruction() noexcept(std::is_nothrow_destructible::value) { destructor(); } protected: void destructor() noexcept(std::is_nothrow_destructible::value) { auto&& self = static_cast(*this); using DestructingType = typename CRTP::type; if (self) { self.data().~DestructingType(); } } }; template struct LazyDestruction::value>> { protected: void destructor() noexcept {} }; template struct LazyStorageBase : LazyCrtpHelper< CRTP, lazy_stored_type, LazyCopyConstruct, LazyCopyAssign, LazyMoveAssign, LazyMoveConstruct, LazyDestruction> { using type = lazy_stored_type; LazyStorageBase() = default; LazyStorageBase& operator=(LazyStorageBase&&) = default; LazyStorageBase& operator=(const LazyStorageBase&) = default; LazyStorageBase(LazyStorageBase&&) = default; LazyStorageBase(const LazyStorageBase&) = default; ~LazyStorageBase() = default; friend struct LazyMoveAssign>; template void assign(Arg&& arg) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_assignable::value) { auto&& self = static_cast(*this); if (self) { data() = std::forward(arg); } else { emplace(std::forward(arg)); } } template void emplace(Args&&... args) noexcept(std::is_nothrow_constructible::value) { static_cast(*this).reset(); new (&_data) type(std::forward(args)...); } protected: using LazyDestruction>::destructor; type& data() noexcept { return *reinterpret_cast(&_data); } const type& data() const noexcept { return *reinterpret_cast(&_data); } typename std::aligned_storage::type _data; }; template struct LazyStorage : LazyStorageBase, T> { private: using Base = LazyStorageBase, T>; using Base::_data; friend struct LazyStorageBase, T>; public: using Base::data; using Base::emplace; using typename Base::type; LazyStorage() = default; LazyStorage& operator=(LazyStorage&&) = default; LazyStorage& operator=(const LazyStorage&) = default; LazyStorage(LazyStorage&&) = default; LazyStorage(const LazyStorage&) = default; ~LazyStorage() = default; explicit operator bool() const noexcept { return _initialized; } void construct(T&& value) noexcept(std::is_nothrow_constructible::value) { emplace(std::move(value)); } T& value() noexcept { return data(); } void destroy() noexcept(std::is_nothrow_destructible::value) { _initialized = false; Base::destructor(); } private: void reset() noexcept(std::is_nothrow_destructible::value) { Base::destructor(); _initialized = true; } bool _initialized = false; }; template struct LazyStorage : LazyStorageBase, T&> { private: using Base = LazyStorageBase, T&>; using Base::_data; friend struct LazyStorageBase, T&>; public: using Base::data; using Base::emplace; using typename Base::type; LazyStorage() { emplace(nullptr); } LazyStorage& operator=(LazyStorage&&) = default; LazyStorage& operator=(const LazyStorage&) = default; LazyStorage(LazyStorage&&) = default; LazyStorage(const LazyStorage&) = default; ~LazyStorage() = default; explicit operator bool() const noexcept { return data(); } void construct(T& value) noexcept(std::is_nothrow_constructible::value) { emplace(&value); } T& value() noexcept { return *data(); } void destroy() noexcept { data() = nullptr; } private: void reset() noexcept {} }; } // namespace detail } // namespace kgr #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_STORAGE_HPP