Skip to content

Commit

Permalink
New 'storage_for' type. No 'using enum' yet
Browse files Browse the repository at this point in the history
  • Loading branch information
vector-of-bool committed Sep 25, 2023
1 parent 2d8957c commit 50a9a2c
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 11 deletions.
2 changes: 0 additions & 2 deletions src/neo/archetypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,6 @@ using special_member_kind = special_member_kind;

namespace specmem_specials {

using enum special_member_kind;

template <special_member_kind DefaultConstructor,
special_member_kind Destructor,
special_member_kind CopyConstructor,
Expand Down
8 changes: 4 additions & 4 deletions src/neo/detail/special_mems.ipp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// clang-format off
constexpr inline auto U = undeclared;
constexpr inline auto F = defaulted;
constexpr inline auto D = deleted;
constexpr inline auto N = nontrivial;
constexpr inline auto U = special_member_kind::undeclared;
constexpr inline auto F = special_member_kind::defaulted;
constexpr inline auto D = special_member_kind::deleted;
constexpr inline auto N = special_member_kind::nontrivial;
template <typename T> using CR=const T&;
template<>struct S<U,U,U,U,U,U>{};
template<>struct S<F,U,U,U,U,U>{S()=default;};
Expand Down
11 changes: 6 additions & 5 deletions src/neo/immediate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "./fwd.hpp"
#include "./optional.hpp"
#include "./type_traits.hpp"
#include "neo/attrib.hpp"

#include <coroutine>
#include <type_traits>
Expand Down Expand Up @@ -54,18 +55,18 @@ struct immediate_promise_base {

template <typename T>
struct immediate_promise : immediate_promise_base {
nano_opt_storage<T> _value;
storage_for<T> _value;

auto get_return_object() noexcept { return defer_convert<T>{*this}; }

template <convertible_to<T> 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 <>
Expand Down Expand Up @@ -189,7 +190,7 @@ class immediate {
template <typename T>
explicit immediate(const T&) -> immediate<T>;

explicit immediate()->immediate<void>;
explicit immediate() -> immediate<void>;

template <typename T>
constexpr imm_detail::defer_convert<T>::operator immediate<T>() const noexcept {
Expand Down
116 changes: 116 additions & 0 deletions src/neo/storage.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#pragma once

#include "./concepts.hpp"
#include "./object_box.hpp"
#include "./unit.hpp"

#include <memory>

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 <typename T>
class storage_for {
using boxed_t = object_box<T>;

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<boxed_t, const boxed_t&>
= default;

constexpr union_t(union_t&&) noexcept
requires trivially_constructible<boxed_t, boxed_t&&>
= default;

constexpr union_t& operator=(const union_t&) noexcept
requires trivially_assignable<boxed_t&, const boxed_t&>
= default;

constexpr union_t& operator=(union_t&&) noexcept
requires trivially_assignable<boxed_t&, boxed_t&&>
= default;

constexpr ~union_t() = default;
constexpr ~union_t()
requires(not trivially_destructible<boxed_t>)
{}
};

/// @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<T> get() & noexcept { return _p_union._box.get(); }
constexpr add_const_reference_t<T> get() const& noexcept { return _p_union._box.get(); }
constexpr add_rvalue_reference_t<T> get() && noexcept { return NEO_MOVE(_p_union)._box.get(); }
constexpr add_rvalue_reference_t<add_const_t<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 <typename... Args>
requires constructible_from<boxed_t, Args...>
constexpr void
construct(Args&&... args) noexcept(nothrow_constructible_from<boxed_t, Args...>) {
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
110 changes: 110 additions & 0 deletions src/neo/storage.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include "./storage.hpp"

#include "./archetypes.hpp"

#include <catch2/catch.hpp>

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<neo::storage_for<neo::unit>>);
static_assert(neo::empty_type<neo::storage_for<void>>);

// Storage traits:
static_assert(not neo::empty_type<neo::storage_for<trivial>>);
static_assert(not neo::trivial_type<neo::storage_for<trivial_default_constructible>>);
static_assert(neo::trivially_assignable<neo::storage_for<trivial_default_constructible>&,
neo::storage_for<trivial_default_constructible>&>);
static_assert(neo::trivially_assignable<neo::storage_for<trivial_default_constructible>&,
neo::storage_for<trivial_default_constructible> const&>);
static_assert(neo::trivially_assignable<neo::storage_for<trivial_default_constructible>&,
neo::storage_for<trivial_default_constructible>&&>);
static_assert(neo::trivially_assignable<neo::storage_for<not_default_constructible>&,
neo::storage_for<not_default_constructible>&>);
static_assert(neo::trivially_assignable<neo::storage_for<not_default_constructible>&,
neo::storage_for<not_default_constructible> const&>);
static_assert(neo::trivially_assignable<neo::storage_for<not_default_constructible>&,
neo::storage_for<not_default_constructible>&&>);
static_assert(neo::trivially_copyable<neo::storage_for<not_default_constructible>>);

static_assert(neo::trivially_copyable<neo::storage_for<trivial_default_constructible>>);
static_assert(not neo::copyable<neo::storage_for<arch::nontrivial_copy_constructor>>);
static_assert(not neo::copyable<neo::storage_for<arch::nontrivial_copy_assignment>>);
static_assert(not neo::copyable<neo::storage_for<arch::nontrivial_move_constructor>>);
static_assert(not neo::copyable<neo::storage_for<arch::nontrivial_move_assignment>>);

static_assert(neo::trivially_copyable<neo::storage_for<trivial_default_constructible&>>);
static_assert(neo::copyable<neo::storage_for<arch::nontrivial_copy_constructor&>>);
static_assert(neo::copyable<neo::storage_for<arch::nontrivial_copy_assignment&>>);
static_assert(neo::copyable<neo::storage_for<arch::nontrivial_move_constructor&>>);
static_assert(neo::copyable<neo::storage_for<arch::nontrivial_move_assignment&>>);

static_assert(neo::trivially_movable<neo::storage_for<arch::trivially_movable>>);
static_assert(neo::trivially_movable<neo::storage_for<arch::trivially_movable>>);
static_assert(not neo::movable<neo::storage_for<arch::nontrivial_move_constructor>>);
static_assert(not neo::movable<neo::storage_for<arch::nontrivial_move_assignment>>);

static_assert(neo::trivially_movable<neo::storage_for<arch::trivially_movable&>>);
static_assert(neo::trivially_movable<neo::storage_for<arch::trivially_movable&>>);
static_assert(neo::movable<neo::storage_for<arch::nontrivial_move_constructor&>>);
static_assert(neo::movable<neo::storage_for<arch::nontrivial_move_assignment&>>);

static_assert(not neo::trivial_type<neo::storage_for<trivial_default_constructible>>);
static_assert(neo::trivially_assignable<neo::storage_for<trivial_default_constructible>&,
neo::storage_for<trivial_default_constructible>&>);
static_assert(neo::trivially_assignable<neo::storage_for<trivial_default_constructible>&,
neo::storage_for<trivial_default_constructible> const&>);
static_assert(neo::trivially_assignable<neo::storage_for<trivial_default_constructible>&,
neo::storage_for<trivial_default_constructible>&&>);
static_assert(neo::trivially_assignable<neo::storage_for<not_default_constructible>&,
neo::storage_for<not_default_constructible>&>);
static_assert(neo::trivially_assignable<neo::storage_for<not_default_constructible>&,
neo::storage_for<not_default_constructible> const&>);
static_assert(neo::trivially_assignable<neo::storage_for<not_default_constructible>&,
neo::storage_for<not_default_constructible>&&>);
static_assert(neo::trivially_copyable<neo::storage_for<not_default_constructible>>);

constexpr auto get_some_storage(bool do_construct) {
auto x4 = neo::storage_for<not_default_constructible>();
if (do_construct) {
x4.construct(42);
}
return x4;
}

TEST_CASE("Constexpr storage") { //
constexpr auto x0 [[maybe_unused]] = neo::storage_for<void>();
constexpr auto x1 [[maybe_unused]] = neo::storage_for<int>();
constexpr auto x2 [[maybe_unused]] = neo::storage_for<neo::unit>();
constexpr auto x3 [[maybe_unused]] = neo::storage_for<trivial>();
// constexpr auto x4 [[maybe_unused]] = neo::storage_for<trivial_default_constructible>();
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<neo::nano_opt_storage<void>>);
// static_assert(neo::trivially_default_constructible<neo::nano_opt_storage<void>>);

0 comments on commit 50a9a2c

Please sign in to comment.