From ee99c9f34f74ab0720f62b85574862b49418b8e2 Mon Sep 17 00:00:00 2001 From: kamchatka-volcano Date: Sun, 27 Aug 2023 19:49:57 +0300 Subject: [PATCH] -removed a const propagation from sfun::optional_ref; -added sfun::optional_cpref - an optional ref with const propagation; -added sfun::optional_cpref_wrapper; --- include/sfun/optional_ref.h | 229 +++++++++++++++++--------- tests/CMakeLists.txt | 1 + tests/test_optional_cpref.cpp | 296 ++++++++++++++++++++++++++++++++++ tests/test_optional_ref.cpp | 33 ++++ 4 files changed, 479 insertions(+), 80 deletions(-) create mode 100644 tests/test_optional_cpref.cpp diff --git a/include/sfun/optional_ref.h b/include/sfun/optional_ref.h index 3aec930..bd76f48 100644 --- a/include/sfun/optional_ref.h +++ b/include/sfun/optional_ref.h @@ -8,7 +8,12 @@ namespace sfun { -template +enum class const_propagation { + disabled, + enabled +}; + +template class optional_ref { static_assert(!std::is_pointer_v, "optional_ref type can't be a pointer"); static_assert(!std::is_reference_v, "optional_ref type can't be a reference"); @@ -20,7 +25,8 @@ class optional_ref { template< typename TVal, typename TCheck = T, - std::enable_if_t, optional_ref>>* = nullptr> + std::enable_if_t, optional_ref>>* = + nullptr> constexpr optional_ref(TVal& val) noexcept : value_{std::addressof(val)} { @@ -60,11 +66,25 @@ class optional_ref { return value_ != nullptr; } + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> + constexpr T& value() const + { + return deref(value_); + } + + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> constexpr const T& value() const { return deref(value_); } + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> constexpr T& value() { return deref(value_); @@ -75,29 +95,58 @@ class optional_ref { return has_value(); } + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> + constexpr T& operator*() const + { + return deref(value_); + } + + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> constexpr const T& operator*() const { return deref(value_); } + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> constexpr T& operator*() { return deref(value_); } + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> + constexpr T* operator->() const + { + sfun_precondition(value_); + return value_; + } + + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> constexpr const T* operator->() const { sfun_precondition(value_); return value_; } + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> constexpr T* operator->() { sfun_precondition(value_); return value_; } - friend void swap(optional_ref& lhs, optional_ref& rhs) noexcept + friend void swap(optional_ref& lhs, optional_ref& rhs) noexcept { std::swap(lhs.value_, rhs.value_); } @@ -106,194 +155,195 @@ class optional_ref { T* value_ = nullptr; }; -template -bool operator==(const optional_ref& lhs, const optional_ref& rhs) +template +bool operator==(const optional_ref& lhs, const optional_ref& rhs) { return static_cast(lhs) == static_cast(rhs) && (!lhs || *lhs == *rhs); } -template -bool operator!=(const optional_ref& lhs, const optional_ref& rhs) +template +bool operator!=(const optional_ref& lhs, const optional_ref& rhs) { return static_cast(lhs) != static_cast(rhs) || (static_cast(lhs) && *lhs != *rhs); } -template -bool operator<(const optional_ref& lhs, const optional_ref& rhs) +template +bool operator<(const optional_ref& lhs, const optional_ref& rhs) { return static_cast(rhs) && (!lhs || *lhs < *rhs); } -template -bool operator<=(const optional_ref& lhs, const optional_ref& rhs) +template +bool operator<=(const optional_ref& lhs, const optional_ref& rhs) { return !lhs || (static_cast(rhs) && *lhs <= *rhs); } -template -bool operator>(const optional_ref& lhs, const optional_ref& rhs) +template +bool operator>(const optional_ref& lhs, const optional_ref& rhs) { return static_cast(lhs) && (!rhs || *lhs > *rhs); } -template -bool operator>=(const optional_ref& lhs, const optional_ref& rhs) +template +bool operator>=(const optional_ref& lhs, const optional_ref& rhs) { return !rhs || (static_cast(lhs) && *lhs >= *rhs); } -template -bool operator==(const optional_ref& lhs, std::nullopt_t) +template +bool operator==(const optional_ref& lhs, std::nullopt_t) { return !lhs; } -template -bool operator==(std::nullopt_t, const optional_ref& rhs) +template +bool operator==(std::nullopt_t, const optional_ref& rhs) { return !rhs; } -template -bool operator!=(const optional_ref& lhs, std::nullopt_t) +template +bool operator!=(const optional_ref& lhs, std::nullopt_t) { return static_cast(lhs); } -template -bool operator!=(std::nullopt_t, const optional_ref& rhs) +template +bool operator!=(std::nullopt_t, const optional_ref& rhs) { return static_cast(rhs); } -template -bool operator<(const optional_ref&, std::nullopt_t) +template +bool operator<(const optional_ref&, std::nullopt_t) { return false; } -template -bool operator<(std::nullopt_t, const optional_ref& rhs) +template +bool operator<(std::nullopt_t, const optional_ref& rhs) { return static_cast(rhs); } -template -bool operator<=(const optional_ref& lhs, std::nullopt_t) +template +bool operator<=(const optional_ref& lhs, std::nullopt_t) { return !lhs; } -template -bool operator<=(std::nullopt_t, const optional_ref&) +template +bool operator<=(std::nullopt_t, const optional_ref&) { return true; } -template -bool operator>(const optional_ref& lhs, std::nullopt_t) +template +bool operator>(const optional_ref& lhs, std::nullopt_t) { return static_cast(lhs); } -template -bool operator>(std::nullopt_t, const optional_ref&) +template +bool operator>(std::nullopt_t, const optional_ref&) { return false; } -template -bool operator>=(const optional_ref&, std::nullopt_t) +template +bool operator>=(const optional_ref&, std::nullopt_t) { return true; } -template -bool operator>=(std::nullopt_t, const optional_ref& rhs) +template +bool operator>=(std::nullopt_t, const optional_ref& rhs) { return !rhs; } -template -bool operator==(const optional_ref& lhs, const U& rhs) +template +bool operator==(const optional_ref& lhs, const U& rhs) { return lhs && *lhs == rhs; } -template -bool operator==(const U& lhs, const optional_ref& rhs) +template +bool operator==(const U& lhs, const optional_ref& rhs) { return rhs && lhs == *rhs; } -template -bool operator!=(const optional_ref& lhs, const U& rhs) +template +bool operator!=(const optional_ref& lhs, const U& rhs) { return !lhs || *lhs != rhs; } -template -bool operator!=(const U& lhs, const optional_ref& rhs) +template +bool operator!=(const U& lhs, const optional_ref& rhs) { return !rhs || lhs != *rhs; } -template -bool operator<(const optional_ref& lhs, const U& rhs) +template +bool operator<(const optional_ref& lhs, const U& rhs) { return !lhs || *lhs < rhs; } -template -bool operator<(const U& lhs, const optional_ref& rhs) +template +bool operator<(const U& lhs, const optional_ref& rhs) { return rhs && lhs < *rhs; } -template -bool operator<=(const optional_ref& lhs, const U& rhs) +template +bool operator<=(const optional_ref& lhs, const U& rhs) { return !lhs || *lhs <= rhs; } -template -bool operator<=(const U& lhs, const optional_ref& rhs) +template +bool operator<=(const U& lhs, const optional_ref& rhs) { return rhs && lhs <= *rhs; } -template -bool operator>(const optional_ref& lhs, const U& rhs) +template +bool operator>(const optional_ref& lhs, const U& rhs) { return lhs && *lhs > rhs; } -template -bool operator>(const U& lhs, const optional_ref& rhs) +template +bool operator>(const U& lhs, const optional_ref& rhs) { return !rhs || lhs > *rhs; } -template -bool operator>=(const optional_ref& lhs, const U& rhs) +template +bool operator>=(const optional_ref& lhs, const U& rhs) { return lhs && *lhs >= rhs; } -template -bool operator>=(const U& lhs, const optional_ref& rhs) +template +bool operator>=(const U& lhs, const optional_ref& rhs) { return !rhs || lhs >= *rhs; } -template +template class optional_ref_wrapper { + using optional_ref_t = optional_ref; static_assert(!std::is_pointer_v, "optional_ref_wrapper type can't be a pointer"); static_assert(!std::is_reference_v, "optional_ref_wrapper type can't be a reference"); public: optional_ref_wrapper() = default; optional_ref_wrapper(std::nullopt_t) noexcept {}; - optional_ref_wrapper(optional_ref ref) noexcept + optional_ref_wrapper(optional_ref_t ref) noexcept : ref_{std::move(ref)} { } @@ -312,32 +362,33 @@ class optional_ref_wrapper { return *this; } - const optional_ref& get() const noexcept + const optional_ref_t& get() const noexcept { return ref_; } - optional_ref& get() noexcept + optional_ref_t& get() noexcept { return ref_; } - operator optional_ref&() noexcept + operator optional_ref_t&() noexcept { return ref_; } - operator const optional_ref&() const noexcept + operator const optional_ref_t&() const noexcept { return ref_; } private: - optional_ref ref_; + optional_ref_t ref_; }; -template -struct member> { +template +struct member> { + using optional_ref_t = optional_ref; constexpr member() = default; template @@ -351,26 +402,26 @@ struct member> { typename TCheck = T, std::enable_if_t, std::decay_t>>* = nullptr> constexpr member(V& val) noexcept - : value_{optional_ref{val}} + : value_{optional_ref_t{val}} { } - constexpr optional_ref& get() noexcept + constexpr optional_ref_t& get() noexcept { return value_; } - constexpr const optional_ref& get() const noexcept + constexpr const optional_ref_t& get() const noexcept { return value_; } - constexpr operator optional_ref&() noexcept + constexpr operator optional_ref_t&() noexcept { return value_; } - constexpr operator const optional_ref&() const noexcept + constexpr operator const optional_ref_t&() const noexcept { return value_; } @@ -385,16 +436,28 @@ struct member> { return *get(); } - constexpr const auto& operator*() const + constexpr auto& operator*() const { return *get(); } + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> + constexpr T* operator->() const + { + return &get().value(); + } + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> constexpr T* operator->() { return &get().value(); } - + template< + const_propagation mode = constPropagationMode, + std::enable_if_t* = nullptr> constexpr const T* operator->() const { return &get().value(); @@ -437,9 +500,15 @@ struct member> { } private: - optional_ref_wrapper value_; + optional_ref_wrapper value_; }; +template +using optional_cpref = optional_ref; + +template +using optional_cpref_wrapper = optional_ref_wrapper; + } //namespace sfun #endif //SFUN_OPTIONAL_REF_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ffda59b..57c6ed8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,7 @@ project(test_sfun) SealLake_GoogleTest( SOURCES test_optional_ref.cpp + test_optional_cpref.cpp test_precondition.cpp test_string.cpp test_traits.cpp diff --git a/tests/test_optional_cpref.cpp b/tests/test_optional_cpref.cpp new file mode 100644 index 0000000..111861b --- /dev/null +++ b/tests/test_optional_cpref.cpp @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace { + +std::string toString(sfun::optional_cpref num) +{ + if (!num.has_value()) + return "empty"; + return std::to_string(num.value()); +} + +template +constexpr int testConstexprOptRef() +{ + auto num = Num; + auto oNum = sfun::optional_cpref{num}; + return oNum.value(); +} + +template +std::string toString() +{ + return std::to_string(Num); +} + +struct Foo { + int num = 42; +}; + +struct TestObject { + sfun::member> oRef; +}; + +struct FooObject { + sfun::member> oRef; +}; + +TEST(OptionalConstPropagatedRef, Constructor) +{ + auto foo = 42; + auto oRef = sfun::optional_cpref{foo}; + ASSERT_TRUE(oRef.has_value()); + ASSERT_EQ(oRef.value(), 42); + + foo = 99; + ASSERT_EQ(oRef.value(), 99); + + oRef.value() = 7; + ASSERT_EQ(foo, 7); +} + +TEST(OptionalConstPropagatedRef, PointerConstructor) +{ + auto foo = 42; + auto oRef = sfun::optional_cpref{&foo}; + ASSERT_TRUE(oRef.has_value()); + ASSERT_EQ(oRef.value(), 42); + + foo = 99; + ASSERT_EQ(oRef.value(), 99); + + oRef.value() = 7; + ASSERT_EQ(foo, 7); +} + +TEST(OptionalConstPropagatedRef, NulloptConstructor) +{ + auto oRef = sfun::optional_cpref{std::nullopt}; + ASSERT_FALSE(oRef.has_value()); +} + +TEST(OptionalConstPropagatedRef, CopyConstructor) +{ + auto foo = 42; + auto oRef = sfun::optional_cpref{foo}; + auto oRef2 = oRef; + ASSERT_TRUE(oRef2.has_value()); + ASSERT_EQ(oRef2.value(), 42); + + foo = 99; + ASSERT_EQ(oRef2.value(), 99); + + oRef2.value() = 7; + ASSERT_EQ(foo, 7); +} + +TEST(OptionalConstPropagatedRef, MoveConstructor) +{ + auto foo = 42; + auto oRef = sfun::optional_cpref{foo}; + auto oRef2 = std::move(oRef); + ASSERT_TRUE(oRef2.has_value()); + ASSERT_EQ(oRef2.value(), 42); + + foo = 99; + ASSERT_EQ(oRef2.value(), 99); + + oRef2.value() = 7; + ASSERT_EQ(foo, 7); +} + +TEST(OptionalConstPropagatedRef, Emplace) +{ + auto foo = 42; + auto bar = 99; + auto oRef = sfun::optional_cpref{}; + ASSERT_FALSE(oRef.has_value()); + + oRef.emplace(foo); + ASSERT_TRUE(oRef.has_value()); + ASSERT_EQ(oRef.value(), 42); + + foo = 99; + ASSERT_EQ(oRef.value(), 99); + + oRef.value() = 7; + ASSERT_EQ(foo, 7); + + oRef.emplace(bar); + ASSERT_EQ(oRef.value(), 99); +} + +TEST(OptionalConstPropagatedRef, EmplacePointer) +{ + auto foo = 42; + auto bar = 99; + auto oRef = sfun::optional_cpref{}; + ASSERT_FALSE(oRef.has_value()); + + oRef.emplace(&foo); + ASSERT_TRUE(oRef.has_value()); + ASSERT_EQ(oRef.value(), 42); + + foo = 99; + ASSERT_EQ(oRef.value(), 99); + + oRef.value() = 7; + ASSERT_EQ(foo, 7); + + oRef.emplace(&bar); + ASSERT_EQ(oRef.value(), 99); +} + +TEST(OptionalConstPropagatedRef, MemberAccess) +{ + auto foo = Foo{}; + auto oRef = sfun::optional_cpref{foo}; + ASSERT_TRUE(oRef.has_value()); + ASSERT_EQ(oRef->num, 42); +} + +TEST(OptionalConstPropagatedRef, Const) +{ + auto foo = 42; + auto oRef = sfun::optional_cpref{foo}; + ASSERT_TRUE(oRef.has_value()); + ASSERT_EQ(oRef.value(), 42); + + auto oRef2 = oRef; + ASSERT_EQ(oRef2.value(), 42); +} + +TEST(OptionalConstPropagatedRef, AsFunctionArg) +{ + ASSERT_EQ("empty", toString({})); + ASSERT_EQ("empty", toString(std::nullopt)); + auto num = 42; + ASSERT_EQ("42", toString(num)); +} + +TEST(OptionalConstPropagatedRefWrapper, InVector) +{ + + auto vec = std::vector>{}; + auto foo = 42; + auto bar = 99; + auto oRefFoo = sfun::optional_cpref{foo}; + auto oRefBar = sfun::optional_cpref{bar}; + + vec.insert(vec.begin(), oRefFoo); + vec.insert(vec.end(), oRefBar); + ASSERT_EQ(vec.size(), 2); + ASSERT_EQ(vec.at(0).get().value(), 42); + ASSERT_EQ(vec.at(1).get().value(), 99); +} + +TEST(OptionalConstPropagatedRefWrapper, InVectorWithConstRef) +{ + auto vec = std::vector>{}; + auto foo = 42; + auto bar = 99; + const auto oRefFoo = sfun::optional_cpref{foo}; + const auto oRefBar = sfun::optional_cpref{bar}; + + vec.insert(vec.begin(), oRefFoo); + vec.insert(vec.end(), oRefBar); + ASSERT_EQ(vec.size(), 2); + ASSERT_EQ(vec.at(0).get().value(), 42); + ASSERT_EQ(vec.at(1).get().value(), 99); +} + +TEST(OptionalConstPropagatedRefMember, Modify) +{ + auto foo = 42; + auto obj = TestObject{foo}; + auto obj2 = TestObject{}; + ASSERT_TRUE(obj.oRef.get().has_value()); + ASSERT_TRUE(obj.oRef.get()); + ASSERT_TRUE(obj.oRef); + + obj.oRef.get().emplace(foo); + ASSERT_TRUE(obj.oRef); + + ASSERT_TRUE(obj.oRef.get().has_value()); + ASSERT_EQ(obj.oRef.get().value(), 42); + + foo = 99; + ASSERT_EQ(obj.oRef.get().value(), 99); + + obj.oRef.get().value() = 7; + ASSERT_EQ(foo, 7); + + obj2 = obj; + ASSERT_EQ(obj2.oRef.get().value(), 7); +} + +TEST(OptionalConstPropagatedRefMember, Dereference) +{ + auto foo = 42; + auto obj = TestObject{foo}; + ASSERT_TRUE(obj.oRef); + ASSERT_EQ(*obj.oRef, 42); + + foo = 99; + ASSERT_EQ(*obj.oRef, 99); + + *obj.oRef = 7; + ASSERT_EQ(foo, 7); +} + +TEST(OptionalConstPropagatedRefMember, ConstDereference) +{ + auto foo = 42; + const auto obj = TestObject{foo}; + ASSERT_TRUE(obj.oRef); + ASSERT_EQ(*obj.oRef, 42); + + foo = 99; + ASSERT_EQ(*obj.oRef, 99); + + //*obj.oRef = 7; + //ASSERT_EQ(foo, 7); +} + +TEST(OptionalConstPropagatedRefMember, MemberAccess) +{ + auto foo = Foo{}; + auto obj = FooObject{foo}; + ASSERT_TRUE(obj.oRef); + + ASSERT_EQ(obj.oRef->num, 42); + + foo.num = 99; + ASSERT_EQ(obj.oRef->num, 99); + + obj.oRef->num = 7; + ASSERT_EQ(foo.num, 7); +} + +TEST(OptionalConstPropagatedRefMember, ConstMemberAccess) +{ + auto foo = Foo{}; + const auto obj = FooObject{foo}; + ASSERT_TRUE(obj.oRef); + + ASSERT_EQ(obj.oRef->num, 42); + + foo.num = 99; + ASSERT_EQ(obj.oRef->num, 99); + + //obj.oRef->num = 7; + //ASSERT_EQ(foo.num, 7); +} + +TEST(OptionalConstPropagatedRef, Constexpr) +{ + ASSERT_EQ("42", toString()>()); +} + +} //namespace \ No newline at end of file diff --git a/tests/test_optional_ref.cpp b/tests/test_optional_ref.cpp index df3a535..a9e52ba 100644 --- a/tests/test_optional_ref.cpp +++ b/tests/test_optional_ref.cpp @@ -6,6 +6,8 @@ #include #include +namespace { + std::string toString(sfun::optional_ref num) { if (!num.has_value()) @@ -242,6 +244,20 @@ TEST(OptionalRefMember, Dereference) ASSERT_EQ(foo, 7); } +TEST(OptionalRefMember, ConstDereference) +{ + auto foo = 42; + const auto obj = TestObject{foo}; + ASSERT_TRUE(obj.oRef); + ASSERT_EQ(*obj.oRef, 42); + + foo = 99; + ASSERT_EQ(*obj.oRef, 99); + + *obj.oRef = 7; + ASSERT_EQ(foo, 7); +} + TEST(OptionalRefMember, MemberAccess) { auto foo = Foo{}; @@ -257,7 +273,24 @@ TEST(OptionalRefMember, MemberAccess) ASSERT_EQ(foo.num, 7); } +TEST(OptionalRefMember, ConstMemberAccess) +{ + auto foo = Foo{}; + const auto obj = FooObject{foo}; + ASSERT_TRUE(obj.oRef); + + ASSERT_EQ(obj.oRef->num, 42); + + foo.num = 99; + ASSERT_EQ(obj.oRef->num, 99); + + obj.oRef->num = 7; + ASSERT_EQ(foo.num, 7); +} + TEST(OptionalRef, Constexpr) { ASSERT_EQ("42", toString()>()); } + +} //namespace \ No newline at end of file