From 343d872be9f91e5fcb9167021790831458cbf19c Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Fri, 1 Nov 2024 22:56:06 +0100 Subject: More work on option --- asl/meta.hpp | 5 + asl/option.hpp | 263 ++++++++++++++++++++++++++++++++++++++++++--- asl/tests/meta_tests.cpp | 12 +++ asl/tests/option_tests.cpp | 80 +++++++++++++- asl/tests/test_types.hpp | 4 +- 5 files changed, 344 insertions(+), 20 deletions(-) (limited to 'asl') diff --git a/asl/meta.hpp b/asl/meta.hpp index d05b095..d253dda 100644 --- a/asl/meta.hpp +++ b/asl/meta.hpp @@ -67,6 +67,9 @@ template concept trivially_destructible = __is_trivially_destructibl template concept trivially_copyable = __is_trivially_copyable(T); +template +concept convertible = __is_convertible(From, To); + using nullptr_t = decltype(nullptr); template struct _un_const_helper { using type = T; }; @@ -81,6 +84,8 @@ template using un_volatile_t = _un_volatile_helper::type; template using un_cv_t = un_volatile_t>; +template using un_cvref_t = un_ref_t>; + template concept is_void = is_same>; template struct _is_ref_helper { static constexpr bool l = false; static constexpr bool r = false; }; 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 class option; + +namespace option_internal +{ + +template +concept convertible_from_option = + convertible&, T> && + convertible&, T> && + convertible&&, T> && + convertible&&, T>; + +template +concept constructible_from_option = + constructible&> && + constructible&> && + constructible&&> && + constructible&&>; + +template +concept assignable_from_option = + assignable&> && + assignable&> && + assignable&&> && + assignable&&>; + +template +concept convertible_constructible_from_option = + convertible_from_option && constructible_from_option; + + +template +concept convertible_constructible_assignable_from_option = + convertible_constructible_from_option && assignable_from_option; + +} // namespace option_internal + +template +concept is_option = requires +{ + typename T::type; + requires is_same, option>; +}; + template class option { - maybe_uninit m_payload; - bool m_has_value = false; + static constexpr bool kIsTrivial = + trivially_default_constructible && + trivially_copy_constructible && + trivially_move_constructible && + trivially_copy_assignable && + trivially_move_assignable && + trivially_destructible; + + using Storage = select_t>; + + Storage m_payload{}; + bool m_has_value = false; template 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 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 - // NOLINTNEXTLINE(*-explicit-conversions) - constexpr option(U&& value) requires constructible + template + constexpr explicit (!convertible) + option(U&& value) + requires ( + constructible && + !is_same, option> + ) { construct(ASL_FWD(value)); } + + constexpr option(const option& other) + requires copy_constructible && kIsTrivial = default; - constexpr option(const option& other) requires copy_constructible + constexpr option(const option& other) + requires copy_constructible && (!kIsTrivial) { if (other.m_has_value) { @@ -55,7 +133,14 @@ public: } } - constexpr option(option&& other) requires move_constructible + constexpr option(const option& other) + requires (!copy_constructible) = delete; + + constexpr option(option&& other) + requires move_constructible && kIsTrivial = default; + + constexpr option(option&& other) + requires move_constructible && (!kIsTrivial) { if (other.m_has_value) { @@ -63,7 +148,68 @@ public: } } - constexpr option& operator=(const option& other) & requires copy_assignable && copy_constructible + template + constexpr explicit (!convertible) + option(const option& other) + requires ( + constructible && + !option_internal::convertible_constructible_from_option + ) + { + if (other.has_value()) + { + construct(other.value()); + } + } + + template + constexpr explicit (!convertible) + option(option&& other) + requires ( + constructible && + !option_internal::convertible_constructible_from_option + ) + { + if (other.has_value()) + { + construct(ASL_MOVE(other).value()); + } + } + + constexpr option& operator=(nullopt_t) & + { + reset(); + return *this; + } + + template + constexpr option& operator=(U&& value) & + requires ( + assignable && + constructible && + !is_same, 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 || copy_constructible) = delete; + + constexpr option& operator=(const option& other) & + requires copy_assignable && copy_constructible && kIsTrivial = default; + + constexpr option& operator=(const option& other) & + requires copy_assignable && copy_constructible && (!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 && move_constructible + constexpr option& operator=(option&& other) & + requires move_assignable && move_constructible && kIsTrivial = default; + + constexpr option& operator=(option&& other) & + requires move_assignable && move_constructible && (!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 + constexpr option& operator=(const option& other) & + requires ( + constructible && + assignable && + !option_internal::convertible_constructible_assignable_from_option + ) + { + if (other.has_value()) + { + if (m_has_value) + { + assign(other.value()); + } + else + { + construct(other.value()); + } + } + else + { + reset(); + } + + return *this; + } + + template + constexpr option& operator=(option&& other) & + requires ( + constructible && + assignable && + !option_internal::convertible_constructible_assignable_from_option + ) + { + 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) @@ -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(); + } } }; diff --git a/asl/tests/meta_tests.cpp b/asl/tests/meta_tests.cpp index 5de4f8d..4397763 100644 --- a/asl/tests/meta_tests.cpp +++ b/asl/tests/meta_tests.cpp @@ -168,3 +168,15 @@ static_assert(!asl::trivially_copyable); static_assert(!asl::trivially_copyable); static_assert(asl::trivially_copyable); static_assert(asl::trivially_copyable); + +class Base {}; +class Derived : public Base {}; +class C {}; +class D { public: operator C() { return c; } C c; }; +class E { public: template E(T&&) {} }; + +static_assert(asl::convertible); +static_assert(!asl::convertible); +static_assert(asl::convertible); +static_assert(!asl::convertible); +static_assert(asl::convertible); diff --git a/asl/tests/option_tests.cpp b/asl/tests/option_tests.cpp index e7fff8b..dea046e 100644 --- a/asl/tests/option_tests.cpp +++ b/asl/tests/option_tests.cpp @@ -1,6 +1,13 @@ #include "asl/option.hpp" #include "asl/tests/test_types.hpp" -#include "asl/testing/testing.hpp" +#include "asl/testing/testing.hpp" + +class Base {}; +class Derived : public Base {}; + +static_assert(!asl::is_option); +static_assert(asl::is_option>); +static_assert(asl::is_option>); static_assert(asl::trivially_destructible>); static_assert(!asl::trivially_destructible>); @@ -22,8 +29,39 @@ static_assert(!asl::copy_assignable>); static_assert(asl::move_assignable>); static_assert(asl::move_assignable>); +static_assert(asl::move_assignable>); static_assert(!asl::move_assignable>); +static_assert(asl::assignable&, asl::option>); +static_assert(!asl::assignable&, asl::option>); + +static_assert(!asl::convertible, asl::option>); +static_assert(asl::convertible, asl::option>); + +class ExplicitConversion { public: explicit ExplicitConversion(int) {} }; +class ImplicitConversion { public: ImplicitConversion(int) {} }; // NOLINT + +static_assert(!asl::convertible); +static_assert(asl::convertible); + +static_assert(!asl::convertible>); +static_assert(asl::convertible>); + +static_assert(!asl::convertible, asl::option>); +static_assert(asl::convertible, asl::option>); + +static_assert(asl::trivially_copy_constructible>); +static_assert(!asl::trivially_copy_constructible>); + +static_assert(asl::trivially_move_constructible>); +static_assert(!asl::trivially_move_constructible>); + +static_assert(asl::trivially_copy_assignable>); +static_assert(!asl::trivially_copy_assignable>); + +static_assert(asl::trivially_move_assignable>); +static_assert(!asl::trivially_move_assignable>); + ASL_TEST(make_null) { asl::option a; @@ -111,3 +149,43 @@ ASL_TEST(deduction_guide) asl::option opt(45); ASL_TEST_EXPECT(opt.value() == 45); } + +ASL_TEST(convert_copy) +{ + asl::option opt8 = uint8_t{8}; + asl::option opt16 = opt8; + + ASL_TEST_EXPECT(opt16.has_value()); + ASL_TEST_EXPECT(opt16.value() == 8); + + opt8 = uint8_t{10}; + ASL_TEST_EXPECT(opt8.has_value()); + ASL_TEST_EXPECT(opt8.value() == 10); + + opt16 = asl::nullopt; + ASL_TEST_EXPECT(!opt16.has_value()); + + opt16 = opt8; + ASL_TEST_EXPECT(opt16.has_value()); + ASL_TEST_EXPECT(opt16.value() == 10); +} + +ASL_TEST(convert_move) +{ + asl::option opt8 = uint8_t{8}; + asl::option opt16 = ASL_MOVE(opt8); + + ASL_TEST_EXPECT(opt16.has_value()); + ASL_TEST_EXPECT(opt16.value() == 8); + + opt8 = ASL_MOVE(uint8_t{10}); + ASL_TEST_EXPECT(opt8.has_value()); + ASL_TEST_EXPECT(opt8.value() == 10); + + opt16 = asl::nullopt; + ASL_TEST_EXPECT(!opt16.has_value()); + + opt16 = ASL_MOVE(opt8); + ASL_TEST_EXPECT(opt16.has_value()); + ASL_TEST_EXPECT(opt16.value() == 10); +} diff --git a/asl/tests/test_types.hpp b/asl/tests/test_types.hpp index cabc084..f988aec 100644 --- a/asl/tests/test_types.hpp +++ b/asl/tests/test_types.hpp @@ -14,11 +14,11 @@ struct MoveConstructible { MoveConstructible(MoveConstructible&&) {} }; struct TriviallyMoveConstructible { TriviallyMoveConstructible(TriviallyMoveConstructible&&) = default; }; struct NonMoveConstructible { NonMoveConstructible(NonMoveConstructible&&) = delete; }; -struct CopyAssignable { CopyAssignable& operator=(const CopyAssignable&) { return *this; } }; +struct CopyAssignable { CopyAssignable(const CopyAssignable&) {} CopyAssignable& operator=(const CopyAssignable&) { return *this; } }; struct TriviallyCopyAssignable { TriviallyCopyAssignable& operator=(const TriviallyCopyAssignable&) = default; }; struct NonCopyAssignable { NonCopyAssignable& operator=(const NonCopyAssignable&) = delete; }; -struct MoveAssignable { MoveAssignable& operator=(MoveAssignable&&) { return *this; } }; +struct MoveAssignable { MoveAssignable(MoveAssignable&&) {} MoveAssignable& operator=(MoveAssignable&&) { return *this; } }; struct TriviallyMoveAssignable { TriviallyMoveAssignable& operator=(TriviallyMoveAssignable&&) = default; }; struct NonMoveAssignable { NonMoveAssignable& operator=(NonMoveAssignable&&) = delete; }; -- cgit