From 50a9a2ce7dd8b67353c79ece25d5231d5dfb9bb1 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 25 Sep 2023 02:06:30 -0600 Subject: [PATCH] New 'storage_for' type. No 'using enum' yet --- src/neo/archetypes.hpp | 2 - src/neo/detail/special_mems.ipp | 8 +-- src/neo/immediate.hpp | 11 +-- src/neo/storage.hpp | 116 ++++++++++++++++++++++++++++++++ src/neo/storage.test.cpp | 110 ++++++++++++++++++++++++++++++ 5 files changed, 236 insertions(+), 11 deletions(-) create mode 100644 src/neo/storage.hpp create mode 100644 src/neo/storage.test.cpp diff --git a/src/neo/archetypes.hpp b/src/neo/archetypes.hpp index 93ef5da..5d32420 100644 --- a/src/neo/archetypes.hpp +++ b/src/neo/archetypes.hpp @@ -296,8 +296,6 @@ using special_member_kind = special_member_kind; namespace specmem_specials { -using enum special_member_kind; - template using CR=const T&; template<>struct S{}; template<>struct S{S()=default;}; diff --git a/src/neo/immediate.hpp b/src/neo/immediate.hpp index 9cf8a74..6247fd1 100644 --- a/src/neo/immediate.hpp +++ b/src/neo/immediate.hpp @@ -5,6 +5,7 @@ #include "./fwd.hpp" #include "./optional.hpp" #include "./type_traits.hpp" +#include "neo/attrib.hpp" #include #include @@ -54,18 +55,18 @@ struct immediate_promise_base { template struct immediate_promise : immediate_promise_base { - nano_opt_storage _value; + storage_for _value; auto get_return_object() noexcept { return defer_convert{*this}; } template U> void return_value(U&& v) noexcept(noexcept(T(NEO_FWD(v)))) { - new (neo::addressof(_value.value)) T(NEO_FWD(v)); + _value.construct(NEO_FWD(v)); } - ~immediate_promise() { _value.value.~T(); } + ~immediate_promise() { _value.destroy(); } - T& value() noexcept { return _value.value; } + T& value() noexcept { return _value.get(); } }; template <> @@ -189,7 +190,7 @@ class immediate { template explicit immediate(const T&) -> immediate; -explicit immediate()->immediate; +explicit immediate() -> immediate; template constexpr imm_detail::defer_convert::operator immediate() const noexcept { diff --git a/src/neo/storage.hpp b/src/neo/storage.hpp new file mode 100644 index 0000000..926017b --- /dev/null +++ b/src/neo/storage.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include "./concepts.hpp" +#include "./object_box.hpp" +#include "./unit.hpp" + +#include + +namespace neo { + +/** + * @brief Presents constexpr-ready storage for an object of type T. + * + * The storage is has conditionally trivial special member functions, but is + * not trivially default-constructible (required for constexpr). This object + * is conditionally empty if T is empty. + * + * @tparam T Any object type, reference type, void type, or bounded array type. + * + * The storage is not exposed directly, and starts unalive. The value may be + * made "alive" by calling .construct(), and ended by .destroy(). The .get() + * method returns a reference to the live object. + * + * To copy the stored object, use copy_from and assign_from (not construct()) + */ +template +class storage_for { + using boxed_t = object_box; + + union union_t { + /// The initiail "nothing" value + NEO_NO_UNIQUE_ADDRESS unit _unit; + /// The union member of the wrapped type + NEO_NO_UNIQUE_ADDRESS boxed_t _box; + + constexpr union_t() noexcept + : _unit() {} + + constexpr union_t(const union_t&) noexcept + requires trivially_constructible + = default; + + constexpr union_t(union_t&&) noexcept + requires trivially_constructible + = default; + + constexpr union_t& operator=(const union_t&) noexcept + requires trivially_assignable + = default; + + constexpr union_t& operator=(union_t&&) noexcept + requires trivially_assignable + = default; + + constexpr ~union_t() = default; + constexpr ~union_t() + requires(not trivially_destructible) + {} + }; + + /// @private The union used as storage. + NEO_NO_UNIQUE_ADDRESS union_t _p_union; + +public: + /// Copy-construct the value into storage from the other live storage. + constexpr void copy_from(storage_for const& other) { + std::construct_at(&_p_union._box, other._p_union._box); + } + constexpr void copy_from(storage_for&& other) { + std::construct_at(&_p_union._box, NEO_MOVE(other)._p_union._box); + } + /// Assign to the value in live storage from the other live storage. + constexpr void assign_from(storage_for const& other) { _p_union._box = other._p_union._box; } + constexpr void assign_from(storage_for&& other) { + _p_union._box = NEO_MOVE(other)._p_union._box; + } + + /// Obtain the current stored value. + constexpr add_lvalue_reference_t get() & noexcept { return _p_union._box.get(); } + constexpr add_const_reference_t get() const& noexcept { return _p_union._box.get(); } + constexpr add_rvalue_reference_t get() && noexcept { return NEO_MOVE(_p_union)._box.get(); } + constexpr add_rvalue_reference_t> get() const&& noexcept { + return NEO_MOVE(_p_union)._box.get(); + } + + /** + * @brief Construct the value in-place in storage. The storage must not already contain a value. + * + * If this function returns normally, the storage now contains a live object. + */ + template + requires constructible_from + constexpr void + construct(Args&&... args) noexcept(nothrow_constructible_from) { + std::construct_at(NEO_ADDRESSOF(_p_union._box), NEO_FWD(args)...); + } + + /** + * @brief Destroy the value currently in storage. The storage must contain a live object. + * + * Once this function returns, the storage no longer contains a live object. + */ + constexpr void destroy() noexcept { std::destroy_at(NEO_ADDRESSOF(_p_union._box)); } + + /** + * @brief Swap to two stored values. + * + * Both this and other must contain live objects to swap + */ + constexpr void swap(storage_for& other) { + using std::swap; + swap(_p_union._box, other._p_union._box); + } +}; + +} // namespace neo \ No newline at end of file diff --git a/src/neo/storage.test.cpp b/src/neo/storage.test.cpp new file mode 100644 index 0000000..bdddf8f --- /dev/null +++ b/src/neo/storage.test.cpp @@ -0,0 +1,110 @@ +#include "./storage.hpp" + +#include "./archetypes.hpp" + +#include + +namespace arch = neo::arch; + +struct trivial { + int i; +}; + +struct trivial_default_constructible { + int i; +}; + +struct trivial_destructible { + int i = 0; +}; + +struct nontrivial { + int i = 0; + ~nontrivial() {} +}; + +struct not_default_constructible { + int a; + constexpr explicit not_default_constructible(int a_) + : a(a_) {} +}; + +// The storage for an empty type is itself an empty type +static_assert(neo::empty_type>); +static_assert(neo::empty_type>); + +// Storage traits: +static_assert(not neo::empty_type>); +static_assert(not neo::trivial_type>); +static_assert(neo::trivially_assignable&, + neo::storage_for&>); +static_assert(neo::trivially_assignable&, + neo::storage_for const&>); +static_assert(neo::trivially_assignable&, + neo::storage_for&&>); +static_assert(neo::trivially_assignable&, + neo::storage_for&>); +static_assert(neo::trivially_assignable&, + neo::storage_for const&>); +static_assert(neo::trivially_assignable&, + neo::storage_for&&>); +static_assert(neo::trivially_copyable>); + +static_assert(neo::trivially_copyable>); +static_assert(not neo::copyable>); +static_assert(not neo::copyable>); +static_assert(not neo::copyable>); +static_assert(not neo::copyable>); + +static_assert(neo::trivially_copyable>); +static_assert(neo::copyable>); +static_assert(neo::copyable>); +static_assert(neo::copyable>); +static_assert(neo::copyable>); + +static_assert(neo::trivially_movable>); +static_assert(neo::trivially_movable>); +static_assert(not neo::movable>); +static_assert(not neo::movable>); + +static_assert(neo::trivially_movable>); +static_assert(neo::trivially_movable>); +static_assert(neo::movable>); +static_assert(neo::movable>); + +static_assert(not neo::trivial_type>); +static_assert(neo::trivially_assignable&, + neo::storage_for&>); +static_assert(neo::trivially_assignable&, + neo::storage_for const&>); +static_assert(neo::trivially_assignable&, + neo::storage_for&&>); +static_assert(neo::trivially_assignable&, + neo::storage_for&>); +static_assert(neo::trivially_assignable&, + neo::storage_for const&>); +static_assert(neo::trivially_assignable&, + neo::storage_for&&>); +static_assert(neo::trivially_copyable>); + +constexpr auto get_some_storage(bool do_construct) { + auto x4 = neo::storage_for(); + if (do_construct) { + x4.construct(42); + } + return x4; +} + +TEST_CASE("Constexpr storage") { // + constexpr auto x0 [[maybe_unused]] = neo::storage_for(); + constexpr auto x1 [[maybe_unused]] = neo::storage_for(); + constexpr auto x2 [[maybe_unused]] = neo::storage_for(); + constexpr auto x3 [[maybe_unused]] = neo::storage_for(); + // constexpr auto x4 [[maybe_unused]] = neo::storage_for(); + constexpr auto x5 [[maybe_unused]] = get_some_storage(false); + constexpr auto x6 [[maybe_unused]] = get_some_storage(true); + static_assert(x6.get().a == 42); +} + +// static_assert(neo::empty_type>); +// static_assert(neo::trivially_default_constructible>);