Some work on maybe_uninit & option

This commit is contained in:
2024-10-01 23:38:01 +02:00
parent def67bba57
commit 0f2d186fbd
5 changed files with 65 additions and 25 deletions

View File

@ -9,15 +9,3 @@
#else #else
#define ASL_NO_UNIQUE_ADDRESS [[no_unique_address]] #define ASL_NO_UNIQUE_ADDRESS [[no_unique_address]]
#endif #endif
namespace asl
{
struct unsafe
{
unsafe() = delete;
explicit unsafe(auto&&) {}
};
} // namespace asl

View File

@ -1,42 +1,61 @@
#pragma once #pragma once
#include "asl/annotations.hpp"
#include "asl/layout.hpp" #include "asl/layout.hpp"
#include "asl/memory.hpp" #include "asl/memory.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/utility.hpp"
namespace asl namespace asl
{ {
template<is_object T> template<is_object T>
class alignas(align_of<T>) maybe_uninit class maybe_uninit
{ {
char m_storage[size_of<T>]; union
{
alignas(align_of<T>) char m_storage[size_of<T>];
T m_value;
};
public: public:
constexpr void* uninit_ptr() const { return m_storage; } constexpr maybe_uninit() {} // NOLINT(*-member-init)
maybe_uninit(const maybe_uninit&) = delete;
maybe_uninit(maybe_uninit&&) = delete;
maybe_uninit& operator=(const maybe_uninit&) = delete;
maybe_uninit& operator=(maybe_uninit&&) = delete;
constexpr ~maybe_uninit() = default;
constexpr ~maybe_uninit() requires (!trivially_destructible<T>) {}
constexpr void* uninit_ptr() && = delete;
constexpr const void* uninit_ptr() const& { return m_storage; }
constexpr void* uninit_ptr() & { return m_storage; }
// @Safety Pointer must only be accessed when in initialized state. // @Safety Pointer must only be accessed when in initialized state.
constexpr const T* init_ptr(unsafe) const { return reinterpret_cast<const T*>(m_storage); } constexpr T* init_ptr_unsafe() && = delete;
constexpr T* init_ptr(unsafe) { return reinterpret_cast< T*>(m_storage); } constexpr const T* init_ptr_unsafe() const& { return &m_value; }
constexpr T* init_ptr_unsafe() & { return &m_value; }
// @Safety Reference must only be accessed when in initialized state. // @Safety Reference must only be accessed when in initialized state.
constexpr const T& as_init(unsafe) const { return *reinterpret_cast<const T*>(m_storage); } constexpr T& as_init_unsafe() && = delete;
constexpr T& as_init(unsafe) { return *reinterpret_cast< T*>(m_storage); } constexpr const T& as_init_unsafe() const& { return m_value; }
constexpr T& as_init_unsafe() & { return m_value; }
// @Safety Must be called only when in uninitialized state. // @Safety Must be called only when in uninitialized state.
template<typename... Args> template<typename... Args>
inline void init(unsafe, Args&&... args) constexpr void init_unsafe(Args&&... args) &
{ {
new(uninit_ptr()) T(ASL_FWD(args)...); new(uninit_ptr()) T(ASL_FWD(args)...);
} }
// @Safety Must be called only when in initialized state. // @Safety Must be called only when in initialized state.
inline void uninit(unsafe) constexpr void uninit_unsafe() &
{ {
if constexpr (!trivially_destructible<T>) if constexpr (!trivially_destructible<T>)
{ {
init_ptr(unsafe("Caller has checked init state"))->~T(); init_ptr_unsafe()->~T();
} }
} }
}; };

View File

@ -1,7 +1,14 @@
#include "asl/maybe_uninit.hpp" #include "asl/maybe_uninit.hpp"
#include "asl/test_types.hpp"
static_assert(asl::layout::of<int>() == asl::layout::of<asl::maybe_uninit<int>>()); static_assert(asl::layout::of<int>() == asl::layout::of<asl::maybe_uninit<int>>());
static_assert(asl::size_of<int> == asl::size_of<asl::maybe_uninit<int>>); static_assert(asl::size_of<int> == asl::size_of<asl::maybe_uninit<int>>);
static_assert(asl::align_of<int> == asl::align_of<asl::maybe_uninit<int>>); static_assert(asl::align_of<int> == asl::align_of<asl::maybe_uninit<int>>);
int main() { return 0; } static_assert(asl::trivially_destructible<asl::maybe_uninit<TriviallyDestructible>>);
static_assert(!asl::trivially_destructible<asl::maybe_uninit<HasDestructor>>);
int main()
{
return 0;
}

View File

@ -1,13 +1,30 @@
#pragma once #pragma once
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/maybe_uninit.hpp"
namespace asl namespace asl
{ {
struct nullopt_t {};
static constexpr nullopt_t nullopt{};
template<is_object T> template<is_object T>
class option class option
{ {
maybe_uninit<T> m_payload;
bool m_has_value = false;
public:
constexpr option() = default;
constexpr option(nullopt_t) {} // NOLINT(*-explicit-conversions)
constexpr ~option() = default;
constexpr ~option() requires (!trivially_destructible<T>)
{
if (m_has_value) { m_payload.uninit_unsafe(); }
}
}; };
} // namespace asl } // namespace asl

View File

@ -1,3 +1,12 @@
#include "asl/option.hpp" #include "asl/option.hpp"
#include "asl/test_types.hpp"
int main() { return 0; } static_assert(asl::trivially_destructible<asl::option<TriviallyDestructible>>);
static_assert(!asl::trivially_destructible<asl::option<HasDestructor>>);
int main()
{
asl::option<HasDestructor> a;
asl::option<TriviallyDestructible> b;
return 0;
}