Sendit support optional<T&> and Result<T&, E>
This commit is contained in:
parent
293f4d1593
commit
c9826616b5
@ -1,6 +1,7 @@
|
||||
#ifndef OPTIONAL_HPP
|
||||
#define OPTIONAL_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@ -14,14 +15,27 @@ namespace detail {
|
||||
|
||||
static constexpr nullopt_t nullopt;
|
||||
|
||||
template<
|
||||
typename T,
|
||||
typename std::enable_if<
|
||||
!std::is_same<typename std::decay<T>::type, void>::value && !std::is_rvalue_reference<T>::value,
|
||||
int
|
||||
>::type = 0
|
||||
>
|
||||
using well_behaved = std::conditional_t<
|
||||
std::is_reference<T>::value, std::reference_wrapper<std::remove_reference_t<T>>, T
|
||||
>;
|
||||
|
||||
template<
|
||||
typename T,
|
||||
typename std::enable_if<!std::is_same<typename std::decay<T>::type, void>::value, int>::type = 0
|
||||
>
|
||||
class optional {
|
||||
using value_type = well_behaved<T>;
|
||||
|
||||
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<void*>(std::addressof(uvalue))) T(other.uvalue);
|
||||
new (static_cast<void*>(std::addressof(uvalue))) value_type(other.uvalue);
|
||||
}
|
||||
}
|
||||
|
||||
optional(optional&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
noexcept(std::is_nothrow_move_constructible<value_type>::value)
|
||||
: holds_value(other.holds_value)
|
||||
{
|
||||
if(holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
new (static_cast<void*>(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<T>::value && std::is_nothrow_move_constructible<T>::value)
|
||||
noexcept(std::is_nothrow_move_assignable<value_type>::value && std::is_nothrow_move_constructible<value_type>::value)
|
||||
{
|
||||
reset();
|
||||
if(other.holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
new (static_cast<void*>(std::addressof(uvalue))) value_type(std::move(other.uvalue));
|
||||
holds_value = true;
|
||||
}
|
||||
return *this;
|
||||
@ -72,7 +86,7 @@ namespace detail {
|
||||
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
|
||||
>
|
||||
optional(U&& value) : holds_value(true) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
|
||||
new (static_cast<void*>(std::addressof(uvalue))) value_type(std::forward<U>(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<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
other.uvalue.~T();
|
||||
new (static_cast<void*>(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<typename U>
|
||||
NODISCARD T value_or(U&& default_value) const & {
|
||||
return holds_value ? uvalue : static_cast<T>(std::forward<U>(default_value));
|
||||
return holds_value ? static_cast<T>(uvalue) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) && {
|
||||
return holds_value ? std::move(uvalue) : static_cast<T>(std::forward<U>(default_value));
|
||||
return holds_value ? static_cast<T>(std::move(uvalue)) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -13,20 +13,21 @@ namespace cpptrace {
|
||||
namespace detail {
|
||||
template<typename T, typename E, typename std::enable_if<!std::is_same<T, E>::value, int>::type = 0>
|
||||
class Result {
|
||||
using value_type = well_behaved<T>;
|
||||
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<typename U>
|
||||
NODISCARD T value_or(U&& default_value) const & {
|
||||
return has_value() ? value_ : static_cast<T>(std::forward<U>(default_value));
|
||||
return has_value() ? static_cast<T>(value_) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) && {
|
||||
return has_value() ? std::move(value_) : static_cast<T>(std::forward<U>(default_value));
|
||||
return has_value() ? static_cast<T>(std::move(value_)) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
void drop_error() const {
|
||||
|
||||
@ -11,11 +11,19 @@ TEST(OptionalTest, DefaultConstructor) {
|
||||
optional<int> o;
|
||||
EXPECT_FALSE(o.has_value());
|
||||
EXPECT_FALSE(static_cast<bool>(o));
|
||||
|
||||
optional<int&> o1;
|
||||
EXPECT_FALSE(o1.has_value());
|
||||
EXPECT_FALSE(static_cast<bool>(o1));
|
||||
}
|
||||
|
||||
TEST(OptionalTest, ConstructWithNullopt) {
|
||||
optional<int> o(nullopt);
|
||||
EXPECT_FALSE(o.has_value());
|
||||
|
||||
optional<int&> o1(nullopt);
|
||||
EXPECT_FALSE(o1.has_value());
|
||||
EXPECT_FALSE(static_cast<bool>(o1));
|
||||
}
|
||||
|
||||
TEST(OptionalTest, ValueConstructor) {
|
||||
@ -27,6 +35,13 @@ TEST(OptionalTest, ValueConstructor) {
|
||||
optional<int> o2(x);
|
||||
EXPECT_TRUE(o2.has_value());
|
||||
EXPECT_EQ(o2.unwrap(), 100);
|
||||
|
||||
int y = 100;
|
||||
optional<int&> 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<int> o3(nullopt);
|
||||
optional<int> o4(o3);
|
||||
EXPECT_FALSE(o4.has_value());
|
||||
|
||||
int y = 100;
|
||||
optional<int&> o5(y);
|
||||
optional<int&> 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<int> o3(nullopt);
|
||||
optional<int> o4(std::move(o3));
|
||||
EXPECT_FALSE(o4.has_value());
|
||||
|
||||
int y = 100;
|
||||
optional<int&> o5(y);
|
||||
optional<int&> 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<int> o4(100);
|
||||
o4 = o3;
|
||||
EXPECT_FALSE(o4.has_value());
|
||||
|
||||
int y = 100;
|
||||
optional<int&> o5(y);
|
||||
optional<int&> 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<int> o4(99);
|
||||
o4 = std::move(o3);
|
||||
EXPECT_FALSE(o4.has_value());
|
||||
|
||||
int y = 100;
|
||||
optional<int&> o5(y);
|
||||
optional<int&> 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<int&> 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<int> o(42);
|
||||
EXPECT_TRUE(o.has_value());
|
||||
EXPECT_EQ(o.unwrap(), 42);
|
||||
o.reset();
|
||||
EXPECT_FALSE(o.has_value());
|
||||
|
||||
int x = 44;
|
||||
optional<int&> 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<int&> o5 = x;
|
||||
optional<int&> 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<int> o2(nullopt);
|
||||
EXPECT_EQ(o2.value_or(100), 100);
|
||||
|
||||
int x = 20;
|
||||
int y = 100;
|
||||
optional<int&> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -45,6 +45,11 @@ TEST_F(ResultFixture, ConstructWithValueLValue) {
|
||||
|
||||
s = "x";
|
||||
EXPECT_EQ(result.unwrap_value(), "test");
|
||||
|
||||
cpptrace::detail::Result<std::string&, error> 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<std::string, error> original(std::string("move"));
|
||||
cpptrace::detail::Result<std::string, error> 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<std::string&, error> r1(s);
|
||||
cpptrace::detail::Result<std::string&, error> 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<int&, error> 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<int&, error> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user