From 007ec1e293ce9e20102739fe6cdeddbe8f590526 Mon Sep 17 00:00:00 2001 From: Billy Robert O'Neal III Date: Thu, 10 Oct 2024 11:39:06 -0700 Subject: [PATCH] Allow constexpr optionals. Extracted from https://github.com/microsoft/vcpkg-tool/pull/1490/ as it's unclear when that change will land. --- include/vcpkg/base/optional.h | 209 ++++++++++++++++++++-------------- src/vcpkg-test/optional.cpp | 4 + 2 files changed, 129 insertions(+), 84 deletions(-) diff --git a/include/vcpkg/base/optional.h b/include/vcpkg/base/optional.h index 12c192957c..c18619deb9 100644 --- a/include/vcpkg/base/optional.h +++ b/include/vcpkg/base/optional.h @@ -23,205 +23,246 @@ namespace vcpkg namespace details { + struct EngageTag + { + }; + + template> + struct OptionalStorageDtor + { + bool m_is_present; + union + { + char m_inactive; + T m_t; + }; + + constexpr OptionalStorageDtor() : m_is_present(false), m_inactive() { } + template + constexpr OptionalStorageDtor(EngageTag, + Args&&... args) noexcept(std::is_nothrow_constructible_v) + : m_is_present(true), m_t(std::forward(args)...) + { + } + }; + + template + struct OptionalStorageDtor + { + bool m_is_present; + union + { + char m_inactive; + T m_t; + }; + + constexpr OptionalStorageDtor() : m_is_present(false), m_inactive() { } + template + constexpr OptionalStorageDtor(EngageTag, + Args&&... args) noexcept(std::is_nothrow_constructible_v) + : m_is_present(true), m_t(std::forward(args)...) + { + } + + ~OptionalStorageDtor() + { + if (m_is_present) + { + m_t.~T(); + } + } + }; + template> - struct OptionalStorage + struct OptionalStorage : OptionalStorageDtor { - constexpr OptionalStorage() noexcept : m_is_present(false), m_inactive() { } + OptionalStorage() = default; constexpr OptionalStorage(const T& t) noexcept(std::is_nothrow_copy_constructible_v) - : m_is_present(true), m_t(t) + : OptionalStorageDtor(EngageTag{}, t) { } constexpr OptionalStorage(T&& t) noexcept(std::is_nothrow_move_constructible_v) - : m_is_present(true), m_t(std::move(t)) + : OptionalStorageDtor(EngageTag{}, std::move(t)) { } template>> explicit OptionalStorage(Optional&& t) noexcept(std::is_nothrow_constructible_v) - : m_is_present(false), m_inactive() + : OptionalStorageDtor() { if (auto p = t.get()) { - m_is_present = true; - new (&m_t) T(std::move(*p)); + this->m_is_present = true; + new (&this->m_t) T(std::move(*p)); } } template explicit OptionalStorage(const Optional& t) noexcept(std::is_nothrow_constructible_v) - : m_is_present(false), m_inactive() + : OptionalStorageDtor() { if (auto p = t.get()) { - m_is_present = true; - new (&m_t) T(*p); + this->m_is_present = true; + new (&this->m_t) T(*p); } } - ~OptionalStorage() - { - if (m_is_present) m_t.~T(); - } - OptionalStorage(const OptionalStorage& o) noexcept(std::is_nothrow_copy_constructible_v) - : m_is_present(o.m_is_present), m_inactive() + : OptionalStorageDtor() { - if (m_is_present) new (&m_t) T(o.m_t); + if (o.m_is_present) + { + this->m_is_present = true; + new (&this->m_t) T(o.m_t); + } } OptionalStorage(OptionalStorage&& o) noexcept(std::is_nothrow_move_constructible_v) - : m_is_present(o.m_is_present), m_inactive() + : OptionalStorageDtor() { - if (m_is_present) + if (o.m_is_present) { - new (&m_t) T(std::move(o.m_t)); + this->m_is_present = true; + new (&this->m_t) T(std::move(o.m_t)); } } OptionalStorage& operator=(const OptionalStorage& o) noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v) { - if (m_is_present && o.m_is_present) + if (this->m_is_present && o.m_is_present) { - m_t = o.m_t; + this->m_t = o.m_t; } - else if (!m_is_present && o.m_is_present) + else if (!this->m_is_present && o.m_is_present) { - new (&m_t) T(o.m_t); - m_is_present = true; + new (&this->m_t) T(o.m_t); + this->m_is_present = true; } - else if (m_is_present && !o.m_is_present) + else if (this->m_is_present && !o.m_is_present) { destroy(); } + return *this; } OptionalStorage& operator=(OptionalStorage&& o) noexcept // enforces termination { - if (m_is_present && o.m_is_present) + if (this->m_is_present && o.m_is_present) { - m_t = std::move(o.m_t); + this->m_t = std::move(o.m_t); } - else if (!m_is_present && o.m_is_present) + else if (!this->m_is_present && o.m_is_present) { - new (&m_t) T(std::move(o.m_t)); - m_is_present = true; + new (&this->m_t) T(std::move(o.m_t)); + this->m_is_present = true; } - else if (m_is_present && !o.m_is_present) + else if (this->m_is_present && !o.m_is_present) { destroy(); } return *this; } - constexpr bool has_value() const noexcept { return m_is_present; } + constexpr bool has_value() const noexcept { return this->m_is_present; } const T& value() const noexcept { return this->m_t; } T& value() noexcept { return this->m_t; } - const T* get() const& noexcept { return m_is_present ? &m_t : nullptr; } - T* get() & noexcept { return m_is_present ? &m_t : nullptr; } + const T* get() const& noexcept { return this->m_is_present ? &this->m_t : nullptr; } + T* get() & noexcept { return this->m_is_present ? &this->m_t : nullptr; } const T* get() const&& = delete; T* get() && = delete; void destroy() noexcept // enforces termination { - m_is_present = false; - m_t.~T(); - m_inactive = '\0'; + this->m_is_present = false; + this->m_t.~T(); + this->m_inactive = '\0'; } template T& emplace(Args&&... args) noexcept(std::is_nothrow_constructible_v) { - if (m_is_present) destroy(); - new (&m_t) T(static_cast(args)...); - m_is_present = true; - return m_t; + if (this->m_is_present) destroy(); + new (&this->m_t) T(static_cast(args)...); + this->m_is_present = true; + return this->m_t; } - - private: - bool m_is_present; - union - { - char m_inactive; - T m_t; - }; }; template - struct OptionalStorage + struct OptionalStorage : OptionalStorageDtor { - constexpr OptionalStorage() noexcept : m_is_present(false), m_inactive() { } + OptionalStorage() = default; constexpr OptionalStorage(T&& t) noexcept(std::is_nothrow_move_constructible_v) - : m_is_present(true), m_t(std::move(t)) + : OptionalStorageDtor(EngageTag{}, std::move(t)) { } - - ~OptionalStorage() + template>> + explicit OptionalStorage(Optional&& t) noexcept(std::is_nothrow_constructible_v) + : OptionalStorageDtor() { - if (m_is_present) m_t.~T(); + if (auto p = t.get()) + { + this->m_is_present = true; + new (&this->m_t) T(std::move(*p)); + } } - OptionalStorage(OptionalStorage&& o) noexcept(std::is_nothrow_move_constructible_v) - : m_is_present(o.m_is_present), m_inactive() + : OptionalStorageDtor() { - if (m_is_present) + if (o.m_is_present) { - new (&m_t) T(std::move(o.m_t)); + this->m_is_present = true; + new (&this->m_t) T(std::move(o.m_t)); } } OptionalStorage& operator=(OptionalStorage&& o) noexcept // enforces termination { - if (m_is_present && o.m_is_present) + if (this->m_is_present && o.m_is_present) { - m_t = std::move(o.m_t); + this->m_t = std::move(o.m_t); } - else if (!m_is_present && o.m_is_present) + else if (!this->m_is_present && o.m_is_present) { - m_is_present = true; - new (&m_t) T(std::move(o.m_t)); + this->m_is_present = true; + new (&this->m_t) T(std::move(o.m_t)); } - else if (m_is_present && !o.m_is_present) + else if (this->m_is_present && !o.m_is_present) { destroy(); } + return *this; } - constexpr bool has_value() const noexcept { return m_is_present; } + constexpr bool has_value() const noexcept { return this->m_is_present; } const T& value() const noexcept { return this->m_t; } T& value() noexcept { return this->m_t; } - const T* get() const& noexcept { return m_is_present ? &m_t : nullptr; } - T* get() & noexcept { return m_is_present ? &m_t : nullptr; } + const T* get() const& noexcept { return this->m_is_present ? &this->m_t : nullptr; } + T* get() & noexcept { return this->m_is_present ? &this->m_t : nullptr; } const T* get() const&& = delete; T* get() && = delete; template T& emplace(Args&&... args) noexcept(std::is_nothrow_constructible_v) { - if (m_is_present) destroy(); - new (&m_t) T(static_cast(args)...); - m_is_present = true; - return m_t; + if (this->m_is_present) destroy(); + new (&this->m_t) T(static_cast(args)...); + this->m_is_present = true; + return this->m_t; } void destroy() noexcept { - m_is_present = false; - m_t.~T(); - m_inactive = '\0'; + this->m_is_present = false; + this->m_t.~T(); + this->m_inactive = '\0'; } - - private: - bool m_is_present; - union - { - char m_inactive; - T m_t; - }; }; template diff --git a/src/vcpkg-test/optional.cpp b/src/vcpkg-test/optional.cpp index 88c3211001..2bb85b642d 100644 --- a/src/vcpkg-test/optional.cpp +++ b/src/vcpkg-test/optional.cpp @@ -65,6 +65,7 @@ TEST_CASE ("value conversion", "[optional]") Optional j = 1; Optional i = j; + (void)i; Optional cstr = "hello, world!"; Optional cppstr = cstr; @@ -89,9 +90,12 @@ TEST_CASE ("optional.map", "[optional]") const Optional> move_only; Optional m = move_only.map([](auto&& p) { return p.get(); }); + (void)m; Optional> n = move_only.map([](auto&& p) -> Optional { return p ? Optional{p.get()} : nullopt; }); + (void)n; Optional o = move_only.map([](auto&&) { return nullopt; }); + (void)o; Optional five = 5;