-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New 'storage_for' type. No 'using enum' yet
- Loading branch information
1 parent
2d8957c
commit 50a9a2c
Showing
5 changed files
with
236 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>>); |