summaryrefslogtreecommitdiff
path: root/asl/option.hpp
diff options
context:
space:
mode:
authorSteven Le Rouzic <steven.lerouzic@gmail.com>2024-11-01 22:56:06 +0100
committerSteven Le Rouzic <steven.lerouzic@gmail.com>2024-12-20 15:35:58 +0100
commit343d872be9f91e5fcb9167021790831458cbf19c (patch)
tree97b233cd71ec254978135eb5847abe514589ceee /asl/option.hpp
parent2a10eaae094e48a157d55ec886aaa07b0d0be6c9 (diff)
More work on option
Diffstat (limited to 'asl/option.hpp')
-rw-r--r--asl/option.hpp263
1 files changed, 246 insertions, 17 deletions
diff --git a/asl/option.hpp b/asl/option.hpp
index ddd531d..2651a00 100644
--- a/asl/option.hpp
+++ b/asl/option.hpp
@@ -15,39 +15,117 @@ static constexpr nullopt_t nullopt{};
// @Todo(option) Function
// @Todo(option) Arrays
+template<is_object T> class option;
+
+namespace option_internal
+{
+
+template<typename T, typename U>
+concept convertible_from_option =
+ convertible<option<U>&, T> &&
+ convertible<const option<U>&, T> &&
+ convertible<option<U>&&, T> &&
+ convertible<const option<U>&&, T>;
+
+template<typename T, typename U>
+concept constructible_from_option =
+ constructible<T, option<U>&> &&
+ constructible<T, const option<U>&> &&
+ constructible<T, option<U>&&> &&
+ constructible<T, const option<U>&&>;
+
+template<typename T, typename U>
+concept assignable_from_option =
+ assignable<T&, option<U>&> &&
+ assignable<T&, const option<U>&> &&
+ assignable<T&, option<U>&&> &&
+ assignable<T&, const option<U>&&>;
+
+template<typename T, typename U>
+concept convertible_constructible_from_option =
+ convertible_from_option<T, U> && constructible_from_option<T, U>;
+
+
+template<typename T, typename U>
+concept convertible_constructible_assignable_from_option =
+ convertible_constructible_from_option<T, U> && assignable_from_option<T, U>;
+
+} // namespace option_internal
+
+template<typename T>
+concept is_option = requires
+{
+ typename T::type;
+ requires is_same<un_cvref_t<T>, option<typename T::type>>;
+};
+
template<is_object T>
class option
{
- maybe_uninit<T> m_payload;
- bool m_has_value = false;
+ static constexpr bool kIsTrivial =
+ trivially_default_constructible<T> &&
+ trivially_copy_constructible<T> &&
+ trivially_move_constructible<T> &&
+ trivially_copy_assignable<T> &&
+ trivially_move_assignable<T> &&
+ trivially_destructible<T>;
+
+ using Storage = select_t<kIsTrivial, T, maybe_uninit<T>>;
+
+ Storage m_payload{};
+ bool m_has_value = false;
template<typename... Args>
constexpr void construct(Args&&... args) &
{
ASL_ASSERT(!m_has_value);
- m_payload.init_unsafe(ASL_FWD(args)...);
m_has_value = true;
+ if constexpr (kIsTrivial)
+ {
+ new(&m_payload) T(ASL_FWD(args)...);
+ }
+ else
+ {
+ m_payload.init_unsafe(ASL_FWD(args)...);
+ }
}
template<typename Arg>
constexpr void assign(Arg&& arg) &
{
ASL_ASSERT(m_has_value);
- m_payload.as_init_unsafe() = ASL_FWD(arg);
+ if constexpr (kIsTrivial)
+ {
+ m_payload = ASL_FWD(arg);
+ }
+ else
+ {
+ m_payload.as_init_unsafe() = ASL_FWD(arg);
+ }
}
public:
+ using type = T;
+
constexpr option() = default;
constexpr option(nullopt_t) {} // NOLINT(*-explicit-conversions)
- template<typename U>
- // NOLINTNEXTLINE(*-explicit-conversions)
- constexpr option(U&& value) requires constructible<T, U>
+ template<typename U = T>
+ constexpr explicit (!convertible<U&&, T>)
+ option(U&& value)
+ requires (
+ constructible<T, U> &&
+ !is_same<un_cvref_t<U>, option>
+ )
{
construct(ASL_FWD(value));
}
+
+ constexpr option(const option& other)
+ requires copy_constructible<T> && kIsTrivial = default;
- constexpr option(const option& other) requires copy_constructible<T>
+ constexpr option(const option& other)
+ requires copy_constructible<T> && (!kIsTrivial)
{
if (other.m_has_value)
{
@@ -55,7 +133,14 @@ public:
}
}
- constexpr option(option&& other) requires move_constructible<T>
+ constexpr option(const option& other)
+ requires (!copy_constructible<T>) = delete;
+
+ constexpr option(option&& other)
+ requires move_constructible<T> && kIsTrivial = default;
+
+ constexpr option(option&& other)
+ requires move_constructible<T> && (!kIsTrivial)
{
if (other.m_has_value)
{
@@ -63,7 +148,68 @@ public:
}
}
- constexpr option& operator=(const option& other) & requires copy_assignable<T> && copy_constructible<T>
+ template<typename U>
+ constexpr explicit (!convertible<const U&, T>)
+ option(const option<U>& other)
+ requires (
+ constructible<T, const U&> &&
+ !option_internal::convertible_constructible_from_option<T, U>
+ )
+ {
+ if (other.has_value())
+ {
+ construct(other.value());
+ }
+ }
+
+ template<typename U>
+ constexpr explicit (!convertible<U&&, T>)
+ option(option<U>&& other)
+ requires (
+ constructible<T, U&&> &&
+ !option_internal::convertible_constructible_from_option<T, U>
+ )
+ {
+ if (other.has_value())
+ {
+ construct(ASL_MOVE(other).value());
+ }
+ }
+
+ constexpr option& operator=(nullopt_t) &
+ {
+ reset();
+ return *this;
+ }
+
+ template<typename U = T>
+ constexpr option& operator=(U&& value) &
+ requires (
+ assignable<T&, U> &&
+ constructible<T, U> &&
+ !is_same<un_cvref_t<U>, option>
+ )
+ {
+ if (m_has_value)
+ {
+ assign(ASL_FWD(value));
+ }
+ else
+ {
+ construct(ASL_FWD(value));
+ }
+
+ return *this;
+ }
+
+ constexpr option& operator=(const option& other) &
+ requires (!copy_assignable<T> || copy_constructible<T>) = delete;
+
+ constexpr option& operator=(const option& other) &
+ requires copy_assignable<T> && copy_constructible<T> && kIsTrivial = default;
+
+ constexpr option& operator=(const option& other) &
+ requires copy_assignable<T> && copy_constructible<T> && (!kIsTrivial)
{
if (&other == this) { return *this; }
@@ -78,7 +224,7 @@ public:
construct(other.m_payload.as_init_unsafe());
}
}
- else if (m_has_value)
+ else
{
reset();
}
@@ -86,7 +232,11 @@ public:
return *this;
}
- constexpr option& operator=(option&& other) & requires move_assignable<T> && move_constructible<T>
+ constexpr option& operator=(option&& other) &
+ requires move_assignable<T> && move_constructible<T> && kIsTrivial = default;
+
+ constexpr option& operator=(option&& other) &
+ requires move_assignable<T> && move_constructible<T> && (!kIsTrivial)
{
if (&other == this) { return *this; }
@@ -101,13 +251,67 @@ public:
construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
}
}
- else if (m_has_value)
+ else
{
reset();
}
return *this;
}
+
+ template<typename U = T>
+ constexpr option& operator=(const option<U>& other) &
+ requires (
+ constructible<T, const U&> &&
+ assignable<T&, const U&> &&
+ !option_internal::convertible_constructible_assignable_from_option<T, U>
+ )
+ {
+ if (other.has_value())
+ {
+ if (m_has_value)
+ {
+ assign(other.value());
+ }
+ else
+ {
+ construct(other.value());
+ }
+ }
+ else
+ {
+ reset();
+ }
+
+ return *this;
+ }
+
+ template<typename U = T>
+ constexpr option& operator=(option<U>&& other) &
+ requires (
+ constructible<T, U> &&
+ assignable<T&, U> &&
+ !option_internal::convertible_constructible_assignable_from_option<T, U>
+ )
+ {
+ if (other.has_value())
+ {
+ if (m_has_value)
+ {
+ assign(ASL_MOVE(other).value());
+ }
+ else
+ {
+ construct(ASL_MOVE(other).value());
+ }
+ }
+ else
+ {
+ reset();
+ }
+
+ return *this;
+ }
constexpr ~option() = default;
constexpr ~option() requires (!trivially_destructible<T>)
@@ -117,7 +321,11 @@ public:
constexpr void reset()
{
- if (m_has_value)
+ if constexpr (kIsTrivial)
+ {
+ m_has_value = false;
+ }
+ else if (m_has_value)
{
m_payload.uninit_unsafe();
m_has_value = false;
@@ -130,19 +338,40 @@ public:
constexpr T&& value() &&
{
ASL_ASSERT(m_has_value); // @Todo Release assert
- return ASL_MOVE(m_payload).as_init_unsafe();
+ if constexpr (kIsTrivial)
+ {
+ return ASL_MOVE(m_payload);
+ }
+ else
+ {
+ return ASL_MOVE(m_payload).as_init_unsafe();
+ }
}
constexpr T& value() &
{
ASL_ASSERT(m_has_value); // @Todo Release assert
- return m_payload.as_init_unsafe();
+ if constexpr (kIsTrivial)
+ {
+ return m_payload;
+ }
+ else
+ {
+ return m_payload.as_init_unsafe();
+ }
}
constexpr const T& value() const&
{
ASL_ASSERT(m_has_value); // @Todo Release assert
- return m_payload.as_init_unsafe();
+ if constexpr (kIsTrivial)
+ {
+ return m_payload;
+ }
+ else
+ {
+ return m_payload.as_init_unsafe();
+ }
}
};