diff --git a/include/util.hh b/include/util.hh index 4dba281..d259e9e 100644 --- a/include/util.hh +++ b/include/util.hh @@ -100,4 +100,97 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { } __builtin_unreachable(); } + // Returns an anonymous union with inavtive field: `T value` and active field `assume init` (no unique address). + template requires(!std::is_reference_v) + constexpr auto uninit() noexcept { + union UI { + struct assume{}; + T value; + [[no_unique_address]] assume init{}; + }; + return UI{}; + } + template requires(!std::is_reference_v) + struct alignas(T) maybe_uninit { + using uninit = std::array; + + constexpr maybe_uninit() noexcept {} + + + constexpr maybe_uninit(const maybe_uninit& c) noexcept + : m_uninit(c.m_uninit) {} + constexpr maybe_uninit(maybe_uninit&& m) noexcept + : m_uninit(std::move(m.m_uninit)) {} + + constexpr maybe_uninit& operator=(maybe_uninit const& c) noexcept + { m_uninit = c.m_uninit; return *this; } + + constexpr maybe_uninit& operator=(maybe_uninit&& c) noexcept + { m_uninit = std::move(c.m_uninit); return *this; } + + constexpr T& operator=(T&& value) noexcept { + m_init = std::move(value); + } + constexpr T& operator=(T const& copy) noexcept { + m_init = maybe_uninit{copy}; + } + + constexpr T& assume_init() & noexcept = delete; + constexpr T&& assume_init() && noexcept { return std::move(m_init); } + constexpr T const& assume_init() const& noexcept { return m_init; } + constexpr T const&& assume_init() const&& noexcept { return m_init; } + + constexpr uninit& data() &noexcept { return m_uninit; } + constexpr uninit&& data() &&noexcept { return std::move(m_uninit); } + constexpr uninit const& data() const&noexcept { return m_uninit; } + constexpr uninit const&& data() const&& noexcept { return std::move(m_uninit); } + + constexpr T* get() noexcept { return std::addressof(m_init); } + constexpr const T* get() const noexcept { return std::addressof(m_init); } + + //TODO: accessors, `assume_init_*()`, etc. + + constexpr ~maybe_uninit() noexcept {} + + constexpr void assume_init_drop() noexcept(std::is_nothrow_destructible_v) + { m_init.~T(); } + + constexpr maybe_uninit init(T&& value) noexcept(std::is_nothrow_move_constructible_v) + { return maybe_uninit{std::move(value), EXPLICIT_INIT_TAG{}}; } + + template requires(std::is_constructible_v) + constexpr maybe_uninit init_in_place(Args&&... args) noexcept(std::is_nothrow_constructible_v) + { return maybe_uninit{EXPLICIT_INIT_TAG{}, std::forward(args)...}; } + + private: + enum EXPLICIT_INIT_TYPE { + EXPLICIT_INIT_IN_PLACE, + EXPLICIT_INIT_MOVE, + }; + template + struct EXPLICIT_INIT_TAG {}; + constexpr maybe_uninit(T&& value, EXPLICIT_INIT_TAG) noexcept(std::is_nothrow_move_constructible_v) + : m_init{std::move(value)} {} + template requires(std::is_constructible_v) + constexpr maybe_uninit(EXPLICIT_INIT_TAG _a, Args&&... args) noexcept(std::is_nothrow_constructible_v) + : m_init{std::forward(args)...} { (void) _a; } + + union { + T m_init; + [[no_unique_address]] alignas(T) uninit m_uninit{}; + }; + }; + + namespace { + constexpr std::string_view ui_test() noexcept { + auto ui = uninit(); + + maybe_uninit mi; + + ui.init.~assume(); + ui.value = std::string_view{"Hello"}; + return std::move(ui.value); + } + static_assert(ui_test()[0] == 'H', "Bad uninit()"); + } } }