diff --git a/src/utils/optional.hpp b/src/utils/optional.hpp index 740a321..ae7031e 100644 --- a/src/utils/optional.hpp +++ b/src/utils/optional.hpp @@ -1,6 +1,7 @@ #ifndef OPTIONAL_HPP #define OPTIONAL_HPP +#include #include #include #include @@ -14,14 +15,27 @@ namespace detail { static constexpr nullopt_t nullopt; + template< + typename T, + typename std::enable_if< + !std::is_same::type, void>::value && !std::is_rvalue_reference::value, + int + >::type = 0 + > + using well_behaved = std::conditional_t< + std::is_reference::value, std::reference_wrapper>, T + >; + template< typename T, typename std::enable_if::type, void>::value, int>::type = 0 > class optional { + using value_type = well_behaved; + union { char x; - T uvalue; + value_type uvalue; }; bool holds_value = false; @@ -37,16 +51,16 @@ namespace detail { optional(const optional& other) : holds_value(other.holds_value) { if(holds_value) { - new (static_cast(std::addressof(uvalue))) T(other.uvalue); + new (static_cast(std::addressof(uvalue))) value_type(other.uvalue); } } optional(optional&& other) - noexcept(std::is_nothrow_move_constructible::value) + noexcept(std::is_nothrow_move_constructible::value) : holds_value(other.holds_value) { if(holds_value) { - new (static_cast(std::addressof(uvalue))) T(std::move(other.uvalue)); + new (static_cast(std::addressof(uvalue))) value_type(std::move(other.uvalue)); } } @@ -57,11 +71,11 @@ namespace detail { } optional& operator=(optional&& other) - noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) + noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) { reset(); if(other.holds_value) { - new (static_cast(std::addressof(uvalue))) T(std::move(other.uvalue)); + new (static_cast(std::addressof(uvalue))) value_type(std::move(other.uvalue)); holds_value = true; } return *this; @@ -72,7 +86,7 @@ namespace detail { typename std::enable_if::type, optional>::value, int>::type = 0 > optional(U&& value) : holds_value(true) { - new (static_cast(std::addressof(uvalue))) T(std::forward(value)); + new (static_cast(std::addressof(uvalue))) value_type(std::forward(value)); } template< @@ -94,11 +108,11 @@ namespace detail { if(holds_value && other.holds_value) { std::swap(uvalue, other.uvalue); } else if(holds_value && !other.holds_value) { - new (&other.uvalue) T(std::move(uvalue)); - uvalue.~T(); + new (&other.uvalue) value_type(std::move(uvalue)); + uvalue.~value_type(); } else if(!holds_value && other.holds_value) { - new (static_cast(std::addressof(uvalue))) T(std::move(other.uvalue)); - other.uvalue.~T(); + new (static_cast(std::addressof(uvalue))) value_type(std::move(other.uvalue)); + other.uvalue.~value_type(); } std::swap(holds_value, other.holds_value); } @@ -113,7 +127,7 @@ namespace detail { void reset() { if(holds_value) { - uvalue.~T(); + uvalue.~value_type(); } holds_value = false; } @@ -140,12 +154,12 @@ namespace detail { template NODISCARD T value_or(U&& default_value) const & { - return holds_value ? uvalue : static_cast(std::forward(default_value)); + return holds_value ? static_cast(uvalue) : static_cast(std::forward(default_value)); } template NODISCARD T value_or(U&& default_value) && { - return holds_value ? std::move(uvalue) : static_cast(std::forward(default_value)); + return holds_value ? static_cast(std::move(uvalue)) : static_cast(std::forward(default_value)); } }; } diff --git a/src/utils/result.hpp b/src/utils/result.hpp index 3e0eee1..75d0694 100644 --- a/src/utils/result.hpp +++ b/src/utils/result.hpp @@ -13,20 +13,21 @@ namespace cpptrace { namespace detail { template::value, int>::type = 0> class Result { + using value_type = well_behaved; union { - T value_; + value_type value_; E error_; }; enum class member { value, error }; member active; public: - Result(T&& value) : value_(std::move(value)), active(member::value) {} + Result(value_type&& value) : value_(std::move(value)), active(member::value) {} Result(E&& error) : error_(std::move(error)), active(member::error) { if(!should_absorb_trace_exceptions()) { std::fprintf(stderr, "%s\n", unwrap_error().what()); } } - Result(T& value) : value_(T(value)), active(member::value) {} + Result(value_type& value) : value_(value_type(value)), active(member::value) {} Result(E& error) : error_(E(error)), active(member::error) { if(!should_absorb_trace_exceptions()) { std::fprintf(stderr, "%s\n", unwrap_error().what()); @@ -34,14 +35,14 @@ namespace detail { } Result(Result&& other) : active(other.active) { if(other.active == member::value) { - new (&value_) T(std::move(other.value_)); + new (&value_) value_type(std::move(other.value_)); } else { new (&error_) E(std::move(other.error_)); } } ~Result() { if(active == member::value) { - value_.~T(); + value_.~value_type(); } else { error_.~E(); } @@ -107,12 +108,12 @@ namespace detail { template NODISCARD T value_or(U&& default_value) const & { - return has_value() ? value_ : static_cast(std::forward(default_value)); + return has_value() ? static_cast(value_) : static_cast(std::forward(default_value)); } template NODISCARD T value_or(U&& default_value) && { - return has_value() ? std::move(value_) : static_cast(std::forward(default_value)); + return has_value() ? static_cast(std::move(value_)) : static_cast(std::forward(default_value)); } void drop_error() const { diff --git a/test/unit/internals/optional.cpp b/test/unit/internals/optional.cpp index 8367bb8..d45fdfe 100644 --- a/test/unit/internals/optional.cpp +++ b/test/unit/internals/optional.cpp @@ -11,11 +11,19 @@ TEST(OptionalTest, DefaultConstructor) { optional o; EXPECT_FALSE(o.has_value()); EXPECT_FALSE(static_cast(o)); + + optional o1; + EXPECT_FALSE(o1.has_value()); + EXPECT_FALSE(static_cast(o1)); } TEST(OptionalTest, ConstructWithNullopt) { optional o(nullopt); EXPECT_FALSE(o.has_value()); + + optional o1(nullopt); + EXPECT_FALSE(o1.has_value()); + EXPECT_FALSE(static_cast(o1)); } TEST(OptionalTest, ValueConstructor) { @@ -27,6 +35,13 @@ TEST(OptionalTest, ValueConstructor) { optional o2(x); EXPECT_TRUE(o2.has_value()); EXPECT_EQ(o2.unwrap(), 100); + + int y = 100; + optional o3(y); + EXPECT_TRUE(o3.has_value()); + EXPECT_EQ(o3.unwrap(), 100); + y = 200; + EXPECT_EQ(o3.unwrap(), 200); } TEST(OptionalTest, CopyConstructor) { @@ -38,6 +53,17 @@ TEST(OptionalTest, CopyConstructor) { optional o3(nullopt); optional o4(o3); EXPECT_FALSE(o4.has_value()); + + int y = 100; + optional o5(y); + optional o6(o5); + EXPECT_TRUE(o5.has_value()); + EXPECT_EQ(o5.unwrap(), 100); + EXPECT_TRUE(o6.has_value()); + EXPECT_EQ(o6.unwrap(), 100); + y = 200; + EXPECT_EQ(o5.unwrap(), 200); + EXPECT_EQ(o6.unwrap(), 200); } TEST(OptionalTest, MoveConstructor) { @@ -49,6 +75,14 @@ TEST(OptionalTest, MoveConstructor) { optional o3(nullopt); optional o4(std::move(o3)); EXPECT_FALSE(o4.has_value()); + + int y = 100; + optional o5(y); + optional o6(std::move(o5)); + EXPECT_TRUE(o6.has_value()); + EXPECT_EQ(o6.unwrap(), 100); + y = 200; + EXPECT_EQ(o6.unwrap(), 200); } TEST(OptionalTest, CopyAssignmentOperator) { @@ -62,6 +96,18 @@ TEST(OptionalTest, CopyAssignmentOperator) { optional o4(100); o4 = o3; EXPECT_FALSE(o4.has_value()); + + int y = 100; + optional o5(y); + optional o6; + o6 = o5; + EXPECT_TRUE(o5.has_value()); + EXPECT_EQ(o5.unwrap(), 100); + EXPECT_TRUE(o6.has_value()); + EXPECT_EQ(o6.unwrap(), 100); + y = 200; + EXPECT_EQ(o5.unwrap(), 200); + EXPECT_EQ(o6.unwrap(), 200); } TEST(OptionalTest, MoveAssignmentOperator) { @@ -75,6 +121,15 @@ TEST(OptionalTest, MoveAssignmentOperator) { optional o4(99); o4 = std::move(o3); EXPECT_FALSE(o4.has_value()); + + int y = 100; + optional o5(y); + optional o6; + o6 = std::move(o5); + EXPECT_TRUE(o6.has_value()); + EXPECT_EQ(o6.unwrap(), 100); + y = 200; + EXPECT_EQ(o6.unwrap(), 200); } TEST(OptionalTest, AssignmentFromValue) { @@ -85,13 +140,31 @@ TEST(OptionalTest, AssignmentFromValue) { o = nullopt; EXPECT_FALSE(o.has_value()); + + optional o1; + int x = 100; + o1 = x; + EXPECT_TRUE(o1.has_value()); + EXPECT_EQ(o1.unwrap(), x); + EXPECT_EQ(&o1.unwrap(), &x); + + o1 = nullopt; + EXPECT_FALSE(o1.has_value()); } TEST(OptionalTest, Reset) { optional o(42); EXPECT_TRUE(o.has_value()); + EXPECT_EQ(o.unwrap(), 42); o.reset(); EXPECT_FALSE(o.has_value()); + + int x = 44; + optional o1(x); + EXPECT_TRUE(o1.has_value()); + EXPECT_EQ(o1.unwrap(), 44); + o1.reset(); + EXPECT_FALSE(o1.has_value()); } TEST(OptionalTest, Swap) { @@ -111,6 +184,18 @@ TEST(OptionalTest, Swap) { EXPECT_FALSE(o3.has_value()); EXPECT_TRUE(o4.has_value()); EXPECT_EQ(o4.unwrap(), 7); + + int x = 20; + int y = 40; + optional o5 = x; + optional o6 = y; + EXPECT_EQ(o5.unwrap(), 20); + EXPECT_EQ(o6.unwrap(), 40); + o5.swap(o6); + EXPECT_EQ(o5.unwrap(), 40); + EXPECT_EQ(o6.unwrap(), 20); + EXPECT_EQ(x, 20); + EXPECT_EQ(y, 40); } TEST(OptionalTest, ValueOr) { @@ -119,6 +204,16 @@ TEST(OptionalTest, ValueOr) { optional o2(nullopt); EXPECT_EQ(o2.value_or(100), 100); + + int x = 20; + int y = 100; + optional o3(x); + EXPECT_EQ(o3.value_or(y), 20); + o3.reset(); + EXPECT_EQ(o3.value_or(y), 100); + EXPECT_EQ(&o3.value_or(y), &y); + EXPECT_EQ(x, 20); + EXPECT_EQ(y, 100); } } diff --git a/test/unit/internals/result.cpp b/test/unit/internals/result.cpp index 640ec79..b68fbf2 100644 --- a/test/unit/internals/result.cpp +++ b/test/unit/internals/result.cpp @@ -45,6 +45,11 @@ TEST_F(ResultFixture, ConstructWithValueLValue) { s = "x"; EXPECT_EQ(result.unwrap_value(), "test"); + + cpptrace::detail::Result r2(s); + EXPECT_EQ(r2.unwrap_value(), "x"); + s = "y"; + EXPECT_EQ(r2.unwrap_value(), "y"); } TEST_F(ResultFixture, ConstructWithErrorRValue) { @@ -71,10 +76,18 @@ TEST_F(ResultFixture, ConstructWithErrorLValue) { TEST_F(ResultFixture, MoveConstructorValue) { cpptrace::detail::Result original(std::string("move")); cpptrace::detail::Result moved(std::move(original)); - EXPECT_TRUE(moved.has_value()); EXPECT_EQ(moved.unwrap_value(), "move"); EXPECT_TRUE(original.has_value()); + + std::string s = "test"; + cpptrace::detail::Result r1(s); + cpptrace::detail::Result r2(std::move(r1)); + EXPECT_TRUE(r2.has_value()); + EXPECT_EQ(r2.unwrap_value(), "test"); + s = "foo"; + EXPECT_EQ(r2.unwrap_value(), "foo"); + EXPECT_TRUE(r2.has_value()); } TEST_F(ResultFixture, MoveConstructorError) { @@ -97,6 +110,20 @@ TEST_F(ResultFixture, ValueOr) { EXPECT_EQ(res_with_error.value_or(-1), -1); EXPECT_EQ(std::move(res_with_error).value_or(-1), -1); } + { + int x = 2; + int y = 3; + cpptrace::detail::Result res_with_value(x); + EXPECT_EQ(res_with_value.value_or(y), 2); + EXPECT_EQ(std::move(res_with_value).value_or(y), 2); + } + { + int x = 2; + cpptrace::detail::Result res_with_error(error{}); + EXPECT_EQ(res_with_error.value_or(x), 2); + EXPECT_EQ(&res_with_error.value_or(x), &x); + EXPECT_EQ(std::move(res_with_error).value_or(x), 2); + } } }