Add android::base::expected
android::base::expected is an Android implementation of the std::expected proposal. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html For usage, refer to the expected.h header file and expected_test.cpp Bug: 132145659 Test: libbase_test Change-Id: I65d3a1ecf8654d9858989755dfd0065c81f7b209
This commit is contained in:
parent
de725e6e75
commit
7d89fb164b
|
@ -131,6 +131,7 @@ cc_test {
|
|||
"cmsg_test.cpp",
|
||||
"endian_test.cpp",
|
||||
"errors_test.cpp",
|
||||
"expected_test.cpp",
|
||||
"file_test.cpp",
|
||||
"logging_test.cpp",
|
||||
"macros_test.cpp",
|
||||
|
|
|
@ -0,0 +1,745 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/expected.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using android::base::expected;
|
||||
using android::base::unexpected;
|
||||
|
||||
typedef expected<int, int> exp_int;
|
||||
typedef expected<double, double> exp_double;
|
||||
typedef expected<std::string, std::string> exp_string;
|
||||
typedef expected<std::pair<std::string, int>, int> exp_pair;
|
||||
|
||||
struct T {
|
||||
int a;
|
||||
int b;
|
||||
T() = default;
|
||||
T(int a, int b) noexcept : a(a), b(b) {}
|
||||
};
|
||||
bool operator==(const T& x, const T& y) {
|
||||
return x.a == y.a && x.b == y.b;
|
||||
}
|
||||
bool operator!=(const T& x, const T& y) {
|
||||
return x.a != y.a || x.b != y.b;
|
||||
}
|
||||
|
||||
struct E {
|
||||
std::string message;
|
||||
int cause;
|
||||
E(const std::string& message, int cause) : message(message), cause(cause) {}
|
||||
};
|
||||
|
||||
typedef expected<T,E> exp_complex;
|
||||
|
||||
TEST(Expected, testDefaultConstructible) {
|
||||
exp_int e;
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ(0, e.value());
|
||||
|
||||
exp_complex e2;
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(T(0,0), e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testCopyConstructible) {
|
||||
exp_int e;
|
||||
exp_int e2 = e;
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(0, e.value());
|
||||
EXPECT_EQ(0, e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testMoveConstructible) {
|
||||
exp_int e;
|
||||
exp_int e2 = std::move(e);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(0, e.value());
|
||||
EXPECT_EQ(0, e2.value());
|
||||
|
||||
exp_string e3(std::string("hello"));
|
||||
exp_string e4 = std::move(e3);
|
||||
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
EXPECT_EQ("", e3.value()); // e3 is moved
|
||||
EXPECT_EQ("hello", e4.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testCopyConstructibleFromConvertibleType) {
|
||||
exp_double e = 3.3f;
|
||||
exp_int e2 = e;
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(3.3f, e.value());
|
||||
EXPECT_EQ(3, e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testMoveConstructibleFromConvertibleType) {
|
||||
exp_double e = 3.3f;
|
||||
exp_int e2 = std::move(e);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(3.3f, e.value());
|
||||
EXPECT_EQ(3, e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleFromValue) {
|
||||
exp_int e = 3;
|
||||
exp_double e2 = 5.5f;
|
||||
exp_string e3 = std::string("hello");
|
||||
exp_complex e4 = T(10, 20);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
EXPECT_EQ(3, e.value());
|
||||
EXPECT_EQ(5.5f, e2.value());
|
||||
EXPECT_EQ("hello", e3.value());
|
||||
EXPECT_EQ(T(10,20), e4.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleFromMovedValue) {
|
||||
std::string hello = "hello";
|
||||
exp_string e = std::move(hello);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ("hello", e.value());
|
||||
EXPECT_EQ("", hello);
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleFromConvertibleValue) {
|
||||
exp_int e = 3.3f; // double to int
|
||||
exp_string e2 = "hello"; // char* to std::string
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ(3, e.value());
|
||||
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ("hello", e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleFromUnexpected) {
|
||||
exp_int::unexpected_type unexp = unexpected(10);
|
||||
exp_int e = unexp;
|
||||
|
||||
exp_double::unexpected_type unexp2 = unexpected(10.5f);
|
||||
exp_double e2 = unexp2;
|
||||
|
||||
exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
|
||||
exp_string e3 = unexp3;
|
||||
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_FALSE(e3.has_value());
|
||||
EXPECT_EQ(10, e.error());
|
||||
EXPECT_EQ(10.5f, e2.error());
|
||||
EXPECT_EQ("error", e3.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testMoveConstructibleFromUnexpected) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_double e2 = unexpected(10.5f);
|
||||
exp_string e3 = unexpected(std::string("error"));
|
||||
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_FALSE(e3.has_value());
|
||||
EXPECT_EQ(10, e.error());
|
||||
EXPECT_EQ(10.5f, e2.error());
|
||||
EXPECT_EQ("error", e3.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleByForwarding) {
|
||||
exp_string e(std::in_place, 5, 'a');
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ("aaaaa", e.value());
|
||||
|
||||
exp_string e2({'a', 'b', 'c'});
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ("abc", e2.value());
|
||||
|
||||
exp_pair e3({"hello", 30});
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_EQ("hello",e3->first);
|
||||
EXPECT_EQ(30,e3->second);
|
||||
}
|
||||
|
||||
TEST(Expected, testDestructible) {
|
||||
bool destroyed = false;
|
||||
struct T {
|
||||
bool* flag_;
|
||||
T(bool* flag) : flag_(flag) {}
|
||||
~T() { *flag_ = true; }
|
||||
};
|
||||
{
|
||||
expected<T, int> exp = T(&destroyed);
|
||||
}
|
||||
EXPECT_TRUE(destroyed);
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignable) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = 20;
|
||||
e = e2;
|
||||
|
||||
EXPECT_EQ(20, e.value());
|
||||
EXPECT_EQ(20, e2.value());
|
||||
|
||||
exp_int e3 = 10;
|
||||
exp_int e4 = 20;
|
||||
e3 = std::move(e4);
|
||||
|
||||
EXPECT_EQ(20, e3.value());
|
||||
EXPECT_EQ(20, e4.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignableFromValue) {
|
||||
exp_int e = 10;
|
||||
e = 20;
|
||||
EXPECT_EQ(20, e.value());
|
||||
|
||||
exp_double e2 = 3.5f;
|
||||
e2 = 10.5f;
|
||||
EXPECT_EQ(10.5f, e2.value());
|
||||
|
||||
exp_string e3 = "hello";
|
||||
e3 = "world";
|
||||
EXPECT_EQ("world", e3.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignableFromUnexpected) {
|
||||
exp_int e = 10;
|
||||
e = unexpected(30);
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_EQ(30, e.error());
|
||||
|
||||
exp_double e2 = 3.5f;
|
||||
e2 = unexpected(10.5f);
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_EQ(10.5f, e2.error());
|
||||
|
||||
exp_string e3 = "hello";
|
||||
e3 = unexpected("world");
|
||||
EXPECT_FALSE(e3.has_value());
|
||||
EXPECT_EQ("world", e3.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignableFromMovedValue) {
|
||||
std::string world = "world";
|
||||
exp_string e = "hello";
|
||||
e = std::move(world);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ("world", e.value());
|
||||
EXPECT_EQ("", world);
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignableFromMovedUnexpected) {
|
||||
std::string world = "world";
|
||||
exp_string e = "hello";
|
||||
e = unexpected(std::move(world));
|
||||
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_EQ("world", e.error());
|
||||
EXPECT_EQ("", world);
|
||||
}
|
||||
|
||||
TEST(Expected, testEmplace) {
|
||||
struct T {
|
||||
int a;
|
||||
double b;
|
||||
T() {}
|
||||
T(int a, double b) noexcept : a(a), b(b) {}
|
||||
};
|
||||
expected<T, int> exp;
|
||||
T& t = exp.emplace(3, 10.5f);
|
||||
|
||||
EXPECT_TRUE(exp.has_value());
|
||||
EXPECT_EQ(3, t.a);
|
||||
EXPECT_EQ(10.5f, t.b);
|
||||
EXPECT_EQ(3, exp.value().a);
|
||||
EXPECT_EQ(10.5, exp.value().b);
|
||||
}
|
||||
|
||||
TEST(Expected, testSwapExpectedExpected) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = 20;
|
||||
e.swap(e2);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(20, e.value());
|
||||
EXPECT_EQ(10, e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testSwapUnexpectedUnexpected) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int e2 = unexpected(20);
|
||||
e.swap(e2);
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_EQ(20, e.error());
|
||||
EXPECT_EQ(10, e2.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testSwapExpectedUnepected) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = unexpected(30);
|
||||
e.swap(e2);
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(30, e.error());
|
||||
EXPECT_EQ(10, e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testDereference) {
|
||||
struct T {
|
||||
int a;
|
||||
double b;
|
||||
T() {}
|
||||
T(int a, double b) : a(a), b(b) {}
|
||||
};
|
||||
expected<T, int> exp = T(3, 10.5f);
|
||||
|
||||
EXPECT_EQ(3, exp->a);
|
||||
EXPECT_EQ(10.5f, exp->b);
|
||||
|
||||
EXPECT_EQ(3, (*exp).a);
|
||||
EXPECT_EQ(10.5f, (*exp).b);
|
||||
}
|
||||
|
||||
TEST(Expected, testTest) {
|
||||
exp_int e = 10;
|
||||
EXPECT_TRUE(e);
|
||||
EXPECT_TRUE(e.has_value());
|
||||
|
||||
exp_int e2 = unexpected(10);
|
||||
EXPECT_FALSE(e2);
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testGetValue) {
|
||||
exp_int e = 10;
|
||||
EXPECT_EQ(10, e.value());
|
||||
EXPECT_EQ(10, e.value_or(20));
|
||||
|
||||
exp_int e2 = unexpected(10);
|
||||
EXPECT_EQ(10, e2.error());
|
||||
EXPECT_EQ(20, e2.value_or(20));
|
||||
}
|
||||
|
||||
TEST(Expected, testSameValues) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = 10;
|
||||
EXPECT_TRUE(e == e2);
|
||||
EXPECT_TRUE(e2 == e);
|
||||
EXPECT_FALSE(e != e2);
|
||||
EXPECT_FALSE(e2 != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testDifferentValues) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = 20;
|
||||
EXPECT_FALSE(e == e2);
|
||||
EXPECT_FALSE(e2 == e);
|
||||
EXPECT_TRUE(e != e2);
|
||||
EXPECT_TRUE(e2 != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testValueWithError) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = unexpected(10);
|
||||
EXPECT_FALSE(e == e2);
|
||||
EXPECT_FALSE(e2 == e);
|
||||
EXPECT_TRUE(e != e2);
|
||||
EXPECT_TRUE(e2 != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testSameErrors) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int e2 = unexpected(10);
|
||||
EXPECT_TRUE(e == e2);
|
||||
EXPECT_TRUE(e2 == e);
|
||||
EXPECT_FALSE(e != e2);
|
||||
EXPECT_FALSE(e2 != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testDifferentErrors) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int e2 = unexpected(20);
|
||||
EXPECT_FALSE(e == e2);
|
||||
EXPECT_FALSE(e2 == e);
|
||||
EXPECT_TRUE(e != e2);
|
||||
EXPECT_TRUE(e2 != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testCompareWithSameValue) {
|
||||
exp_int e = 10;
|
||||
int value = 10;
|
||||
EXPECT_TRUE(e == value);
|
||||
EXPECT_TRUE(value == e);
|
||||
EXPECT_FALSE(e != value);
|
||||
EXPECT_FALSE(value != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testCompareWithDifferentValue) {
|
||||
exp_int e = 10;
|
||||
int value = 20;
|
||||
EXPECT_FALSE(e == value);
|
||||
EXPECT_FALSE(value == e);
|
||||
EXPECT_TRUE(e != value);
|
||||
EXPECT_TRUE(value != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testCompareWithSameError) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int::unexpected_type error = 10;
|
||||
EXPECT_TRUE(e == error);
|
||||
EXPECT_TRUE(error == e);
|
||||
EXPECT_FALSE(e != error);
|
||||
EXPECT_FALSE(error != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testCompareWithDifferentError) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int::unexpected_type error = 20;
|
||||
EXPECT_FALSE(e == error);
|
||||
EXPECT_FALSE(error == e);
|
||||
EXPECT_TRUE(e != error);
|
||||
EXPECT_TRUE(error != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testDivideExample) {
|
||||
struct QR {
|
||||
int quotient;
|
||||
int remainder;
|
||||
QR(int q, int r) noexcept : quotient(q), remainder(r) {}
|
||||
bool operator==(const QR& rhs) const {
|
||||
return quotient == rhs.quotient && remainder == rhs.remainder;
|
||||
}
|
||||
bool operator!=(const QR& rhs) const {
|
||||
return quotient != rhs.quotient || remainder == rhs.remainder;
|
||||
}
|
||||
};
|
||||
|
||||
auto divide = [](int x, int y) -> expected<QR,E> {
|
||||
if (y == 0) {
|
||||
return unexpected(E("divide by zero", -1));
|
||||
} else {
|
||||
return QR(x / y, x % y);
|
||||
}
|
||||
};
|
||||
|
||||
EXPECT_FALSE(divide(10, 0));
|
||||
EXPECT_EQ("divide by zero", divide(10, 0).error().message);
|
||||
EXPECT_EQ(-1, divide(10, 0).error().cause);
|
||||
|
||||
EXPECT_TRUE(divide(10, 3));
|
||||
EXPECT_EQ(QR(3, 1), divide(10, 3));
|
||||
}
|
||||
|
||||
TEST(Expected, testPair) {
|
||||
auto test = [](bool yes) -> exp_pair {
|
||||
if (yes) {
|
||||
return exp_pair({"yes", 42});
|
||||
} else {
|
||||
return unexpected(42);
|
||||
}
|
||||
};
|
||||
|
||||
auto r = test(true);
|
||||
EXPECT_TRUE(r);
|
||||
EXPECT_EQ("yes", r->first);
|
||||
}
|
||||
|
||||
// copied from result_test.cpp
|
||||
struct ConstructorTracker {
|
||||
static size_t constructor_called;
|
||||
static size_t copy_constructor_called;
|
||||
static size_t move_constructor_called;
|
||||
static size_t copy_assignment_called;
|
||||
static size_t move_assignment_called;
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
|
||||
ConstructorTracker(T&& string) : string(string) {
|
||||
++constructor_called;
|
||||
}
|
||||
ConstructorTracker(const ConstructorTracker& ct) {
|
||||
++copy_constructor_called;
|
||||
string = ct.string;
|
||||
}
|
||||
ConstructorTracker(ConstructorTracker&& ct) noexcept {
|
||||
++move_constructor_called;
|
||||
string = std::move(ct.string);
|
||||
}
|
||||
ConstructorTracker& operator=(const ConstructorTracker& ct) {
|
||||
++copy_assignment_called;
|
||||
string = ct.string;
|
||||
return *this;
|
||||
}
|
||||
ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
|
||||
++move_assignment_called;
|
||||
string = std::move(ct.string);
|
||||
return *this;
|
||||
}
|
||||
static void Reset() {
|
||||
constructor_called = 0;
|
||||
copy_constructor_called = 0;
|
||||
move_constructor_called = 0;
|
||||
copy_assignment_called = 0;
|
||||
move_assignment_called = 0;
|
||||
}
|
||||
std::string string;
|
||||
};
|
||||
|
||||
size_t ConstructorTracker::constructor_called = 0;
|
||||
size_t ConstructorTracker::copy_constructor_called = 0;
|
||||
size_t ConstructorTracker::move_constructor_called = 0;
|
||||
size_t ConstructorTracker::copy_assignment_called = 0;
|
||||
size_t ConstructorTracker::move_assignment_called = 0;
|
||||
|
||||
typedef expected<ConstructorTracker, int> exp_track;
|
||||
|
||||
TEST(Expected, testNumberOfCopies) {
|
||||
// default constructor
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e("hello");
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// copy constructor
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e2 = e;
|
||||
EXPECT_EQ(0U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// move constructor
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e3 = std::move(e);
|
||||
EXPECT_EQ(0U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// construct from lvalue
|
||||
ConstructorTracker::Reset();
|
||||
ConstructorTracker ct = "hello";
|
||||
exp_track e4(ct);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// construct from rvalue
|
||||
ConstructorTracker::Reset();
|
||||
ConstructorTracker ct2 = "hello";
|
||||
exp_track e5(std::move(ct2));
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// copy assignment
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e6 = "hello";
|
||||
exp_track e7 = "world";
|
||||
e7 = e6;
|
||||
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// move assignment
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e8 = "hello";
|
||||
exp_track e9 = "world";
|
||||
e9 = std::move(e8);
|
||||
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// swap
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e10 = "hello";
|
||||
exp_track e11 = "world";
|
||||
std::swap(e10, e11);
|
||||
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(2U, ConstructorTracker::move_assignment_called);
|
||||
}
|
||||
|
||||
TEST(Expected, testNoCopyOnReturn) {
|
||||
auto test = [](const std::string& in) -> exp_track {
|
||||
if (in.empty()) {
|
||||
return "literal string";
|
||||
}
|
||||
if (in == "test2") {
|
||||
return ConstructorTracker(in + in + "2");
|
||||
}
|
||||
ConstructorTracker result(in + " " + in);
|
||||
return result;
|
||||
};
|
||||
|
||||
ConstructorTracker::Reset();
|
||||
auto result1 = test("");
|
||||
ASSERT_TRUE(result1);
|
||||
EXPECT_EQ("literal string", result1->string);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
ConstructorTracker::Reset();
|
||||
auto result2 = test("test2");
|
||||
ASSERT_TRUE(result2);
|
||||
EXPECT_EQ("test2test22", result2->string);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
ConstructorTracker::Reset();
|
||||
auto result3 = test("test3");
|
||||
ASSERT_TRUE(result3);
|
||||
EXPECT_EQ("test3 test3", result3->string);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
}
|
||||
|
||||
TEST(Expected, testNested) {
|
||||
expected<exp_string, std::string> e = "hello";
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e.value().has_value());
|
||||
EXPECT_TRUE(e);
|
||||
EXPECT_TRUE(*e);
|
||||
EXPECT_EQ("hello", e.value().value());
|
||||
|
||||
expected<exp_string, std::string> e2 = unexpected("world");
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_FALSE(e2);
|
||||
EXPECT_EQ("world", e2.error());
|
||||
|
||||
expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_FALSE(e3.value().has_value());
|
||||
EXPECT_TRUE(e3);
|
||||
EXPECT_FALSE(*e3);
|
||||
EXPECT_EQ("world", e3.value().error());
|
||||
}
|
||||
|
||||
constexpr bool equals(const char* a, const char* b) {
|
||||
return (a == nullptr && b == nullptr) ||
|
||||
(a != nullptr && b != nullptr && *a == *b &&
|
||||
(*a == '\0' || equals(a + 1, b + 1)));
|
||||
}
|
||||
|
||||
TEST(Expected, testConstexpr) {
|
||||
// Compliation error will occur if these expressions can't be
|
||||
// evaluated at compile time
|
||||
constexpr exp_int e(3);
|
||||
constexpr exp_int::unexpected_type err(3);
|
||||
constexpr int i = 4;
|
||||
|
||||
// default constructor
|
||||
static_assert(exp_int().value() == 0);
|
||||
// copy constructor
|
||||
static_assert(exp_int(e).value() == 3);
|
||||
// move constructor
|
||||
static_assert(exp_int(exp_int(4)).value() == 4);
|
||||
// copy construct from value
|
||||
static_assert(exp_int(i).value() == 4);
|
||||
// copy construct from unexpected
|
||||
static_assert(exp_int(err).error() == 3);
|
||||
// move costruct from unexpected
|
||||
static_assert(exp_int(unexpected(3)).error() == 3);
|
||||
// observers
|
||||
static_assert(*exp_int(3) == 3);
|
||||
static_assert(exp_int(3).has_value() == true);
|
||||
static_assert(exp_int(3).value_or(4) == 3);
|
||||
|
||||
typedef expected<const char*, int> exp_s;
|
||||
constexpr exp_s s("hello");
|
||||
constexpr const char* c = "hello";
|
||||
static_assert(equals(exp_s().value(), nullptr));
|
||||
static_assert(equals(exp_s(s).value(), "hello"));
|
||||
static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
|
||||
static_assert(equals(exp_s("hello").value(), "hello"));
|
||||
static_assert(equals(exp_s(c).value(), "hello"));
|
||||
}
|
||||
|
||||
TEST(Expected, testWithNonConstructible) {
|
||||
struct AssertNotConstructed {
|
||||
AssertNotConstructed() = delete;
|
||||
};
|
||||
|
||||
expected<int, AssertNotConstructed> v(42);
|
||||
EXPECT_TRUE(v.has_value());
|
||||
EXPECT_EQ(42, v.value());
|
||||
|
||||
expected<AssertNotConstructed, int> e(unexpected(42));
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_EQ(42, e.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testWithMoveOnlyType) {
|
||||
typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
|
||||
exp_ptr e(std::make_unique<int>(3));
|
||||
exp_ptr e2(unexpected(std::make_unique<int>(4)));
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_EQ(3, *(e.value()));
|
||||
EXPECT_EQ(4, *(e2.error()));
|
||||
|
||||
e2 = std::move(e);
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(3, *(e2.value()));
|
||||
}
|
|
@ -0,0 +1,600 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
// android::base::expected is an Android implementation of the std::expected
|
||||
// proposal.
|
||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
|
||||
//
|
||||
// Usage:
|
||||
// using android::base::expected;
|
||||
// using android::base::unexpected;
|
||||
//
|
||||
// expected<double,std::string> safe_divide(double i, double j) {
|
||||
// if (j == 0) return unexpected("divide by zero");
|
||||
// else return i / j;
|
||||
// }
|
||||
//
|
||||
// void test() {
|
||||
// auto q = safe_divide(10, 0);
|
||||
// if (q) { printf("%f\n", q.value()); }
|
||||
// else { printf("%s\n", q.error().c_str()); }
|
||||
// }
|
||||
//
|
||||
// When the proposal becomes part of the standard and is implemented by
|
||||
// libcxx, this will be removed and android::base::expected will be
|
||||
// type alias to std::expected.
|
||||
//
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Synopsis
|
||||
template<class T, class E>
|
||||
class expected;
|
||||
|
||||
template<class E>
|
||||
class unexpected;
|
||||
template<class E>
|
||||
unexpected(E) -> unexpected<E>;
|
||||
|
||||
template<class E>
|
||||
class bad_expected_access;
|
||||
|
||||
template<>
|
||||
class bad_expected_access<void>;
|
||||
|
||||
struct unexpect_t {
|
||||
explicit unexpect_t() = default;
|
||||
};
|
||||
inline constexpr unexpect_t unexpect{};
|
||||
|
||||
// macros for SFINAE
|
||||
#define _ENABLE_IF(...) \
|
||||
, std::enable_if_t<(__VA_ARGS__)>* = nullptr
|
||||
|
||||
// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
|
||||
// ignored when used as a return value. This is off by default.
|
||||
#ifdef NODISCARD_EXPECTED
|
||||
#define _NODISCARD_ [[nodiscard]]
|
||||
#else
|
||||
#define _NODISCARD_
|
||||
#endif
|
||||
|
||||
// Class expected
|
||||
template<class T, class E>
|
||||
class _NODISCARD_ expected {
|
||||
public:
|
||||
using value_type = T;
|
||||
using error_type = E;
|
||||
using unexpected_type = unexpected<E>;
|
||||
|
||||
template<class U>
|
||||
using rebind = expected<U, error_type>;
|
||||
|
||||
// constructors
|
||||
constexpr expected() = default;
|
||||
constexpr expected(const expected& rhs) = default;
|
||||
constexpr expected(expected&& rhs) noexcept = default;
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_constructible_v<T, const U&> &&
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
||||
!std::is_convertible_v<expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
||||
!(!std::is_convertible_v<const U&, T> ||
|
||||
!std::is_convertible_v<const G&, E>) /* non-explicit */
|
||||
)>
|
||||
constexpr expected(const expected<U, G>& rhs) {
|
||||
if (rhs.has_value()) var_ = rhs.value();
|
||||
else var_ = unexpected(rhs.error());
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_constructible_v<T, const U&> &&
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
||||
!std::is_convertible_v<expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
||||
(!std::is_convertible_v<const U&, T> ||
|
||||
!std::is_convertible_v<const G&, E>) /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(const expected<U, G>& rhs) {
|
||||
if (rhs.has_value()) var_ = rhs.value();
|
||||
else var_ = unexpected(rhs.error());
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_constructible_v<T, const U&> &&
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
||||
!std::is_convertible_v<expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
||||
!(!std::is_convertible_v<const U&, T> ||
|
||||
!std::is_convertible_v<const G&, E>) /* non-explicit */
|
||||
)>
|
||||
constexpr expected(expected<U, G>&& rhs) {
|
||||
if (rhs.has_value()) var_ = std::move(rhs.value());
|
||||
else var_ = unexpected(std::move(rhs.error()));
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_constructible_v<T, const U&> &&
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
||||
!std::is_convertible_v<expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
||||
(!std::is_convertible_v<const U&, T> ||
|
||||
!std::is_convertible_v<const G&, E>) /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(expected<U, G>&& rhs) {
|
||||
if (rhs.has_value()) var_ = std::move(rhs.value());
|
||||
else var_ = unexpected(std::move(rhs.error()));
|
||||
}
|
||||
|
||||
template<class U = T _ENABLE_IF(
|
||||
std::is_constructible_v<T, U&&> &&
|
||||
std::is_convertible_v<U&&,T> /* non-explicit */
|
||||
)>
|
||||
constexpr expected(U&& v)
|
||||
: var_(std::in_place_index<0>, std::forward<U>(v)) {}
|
||||
|
||||
template<class U = T _ENABLE_IF(
|
||||
std::is_constructible_v<T, U&&> &&
|
||||
!std::is_convertible_v<U&&,T> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(U&& v)
|
||||
: var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
std::is_convertible_v<const G&, E> /* non-explicit */
|
||||
)>
|
||||
constexpr expected(const unexpected<G>& e)
|
||||
: var_(std::in_place_index<1>, e.value()) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_convertible_v<const G&, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(const unexpected<G>& e)
|
||||
: var_(std::in_place_index<1>, E(e.value())) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, G&&> &&
|
||||
std::is_convertible_v<G&&, E> /* non-explicit */
|
||||
)>
|
||||
constexpr expected(unexpected<G>&& e)
|
||||
: var_(std::in_place_index<1>, std::move(e.value())) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, G&&> &&
|
||||
!std::is_convertible_v<G&&, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(unexpected<G>&& e)
|
||||
: var_(std::in_place_index<1>, E(std::move(e.value()))) {}
|
||||
|
||||
template<class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<T, Args&&...>
|
||||
)>
|
||||
constexpr explicit expected(std::in_place_t, Args&&... args)
|
||||
: var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<T, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
|
||||
|
||||
template<class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<E, Args...>
|
||||
)>
|
||||
constexpr explicit expected(unexpect_t, Args&&... args)
|
||||
: var_(unexpected_type(std::forward<Args>(args)...)) {}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
|
||||
: var_(unexpected_type(il, std::forward<Args>(args)...)) {}
|
||||
|
||||
// destructor
|
||||
~expected() = default;
|
||||
|
||||
// assignment
|
||||
// TODO(b/132145659) enable assignment operator only when the condition
|
||||
// satisfies. SFNAIE doesn't work here because assignment operator should be
|
||||
// non-template. We could workaround this by defining a templated parent class
|
||||
// having the assignment operator. This incomplete implementation however
|
||||
// doesn't allow us to copy assign expected<T,E> even when T is non-copy
|
||||
// assignable. The copy assignment will fail by the underlying std::variant
|
||||
// anyway though the error message won't be clear.
|
||||
// std::enable_if_t<(
|
||||
// std::is_copy_assignable_v<T> &&
|
||||
// std::is_copy_constructible_v<T> &&
|
||||
// std::is_copy_assignable_v<E> &&
|
||||
// std::is_copy_constructible_v<E> &&
|
||||
// (std::is_nothrow_move_constructible_v<E> ||
|
||||
// std::is_nothrow_move_constructible_v<T>)
|
||||
// ), expected&>
|
||||
expected& operator=(const expected& rhs) {
|
||||
var_ = rhs.var_;
|
||||
return *this;
|
||||
}
|
||||
// std::enable_if_t<(
|
||||
// std::is_move_constructible_v<T> &&
|
||||
// std::is_move_assignable_v<T> &&
|
||||
// std::is_nothrow_move_constructible_v<E> &&
|
||||
// std::is_nothrow_move_assignable_v<E>
|
||||
// ), expected&>
|
||||
expected& operator=(expected&& rhs) noexcept {
|
||||
var_ = std::move(rhs.var_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U = T _ENABLE_IF(
|
||||
!std::is_void_v<T> &&
|
||||
std::is_constructible_v<T,U> &&
|
||||
std::is_assignable_v<T&,U> &&
|
||||
std::is_nothrow_move_constructible_v<E>
|
||||
)>
|
||||
expected& operator=(U&& rhs) {
|
||||
var_ = T(std::forward<U>(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class G = E>
|
||||
// TODO: std::is_nothrow_copy_constructible_v<E> && std::is_copy_assignable_v<E>
|
||||
expected& operator=(const unexpected<G>& rhs) {
|
||||
var_ = rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_nothrow_move_constructible_v<G> &&
|
||||
std::is_move_assignable_v<G>
|
||||
)>
|
||||
expected& operator=(unexpected<G>&& rhs) {
|
||||
var_ = std::move(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// modifiers
|
||||
template<class... Args _ENABLE_IF(
|
||||
std::is_nothrow_constructible_v<T, Args...>
|
||||
)>
|
||||
T& emplace(Args&&... args) {
|
||||
expected(std::in_place, std::forward<Args>(args)...).swap(*this);
|
||||
return value();
|
||||
}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
T& emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
|
||||
return value();
|
||||
}
|
||||
|
||||
// swap
|
||||
template<typename U = T, typename = std::enable_if_t<(
|
||||
std::is_swappable_v<U> &&
|
||||
std::is_swappable_v<E> &&
|
||||
(std::is_move_constructible_v<U> ||
|
||||
std::is_move_constructible_v<E>))>>
|
||||
void swap(expected& rhs) noexcept(
|
||||
std::is_nothrow_move_constructible_v<T> &&
|
||||
std::is_nothrow_swappable_v<T> &&
|
||||
std::is_nothrow_move_constructible_v<E> &&
|
||||
std::is_nothrow_swappable_v<E>) {
|
||||
var_.swap(rhs.var_);
|
||||
}
|
||||
|
||||
// observers
|
||||
constexpr const T* operator->() const { return std::addressof(value()); }
|
||||
constexpr T* operator->() { return std::addressof(value()); }
|
||||
constexpr const T& operator*() const& { return value(); }
|
||||
constexpr T& operator*() & { return value(); }
|
||||
constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
|
||||
constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
|
||||
|
||||
constexpr explicit operator bool() const noexcept { return has_value(); }
|
||||
constexpr bool has_value() const noexcept { return var_.index() == 0; }
|
||||
|
||||
constexpr const T& value() const& { return std::get<T>(var_); }
|
||||
constexpr T& value() & { return std::get<T>(var_); }
|
||||
constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
|
||||
constexpr T&& value() && { return std::move(std::get<T>(var_)); }
|
||||
|
||||
constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
|
||||
constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
|
||||
constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
|
||||
constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
|
||||
|
||||
template<class U _ENABLE_IF(
|
||||
std::is_copy_constructible_v<T> &&
|
||||
std::is_convertible_v<U, T>
|
||||
)>
|
||||
constexpr T value_or(U&& v) const& {
|
||||
if (has_value()) return value();
|
||||
else return static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
template<class U _ENABLE_IF(
|
||||
std::is_move_constructible_v<T> &&
|
||||
std::is_convertible_v<U, T>
|
||||
)>
|
||||
constexpr T value_or(U&& v) && {
|
||||
if (has_value()) return std::move(value());
|
||||
else return static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
// expected equality operators
|
||||
template<class T1, class E1, class T2, class E2>
|
||||
friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
|
||||
template<class T1, class E1, class T2, class E2>
|
||||
friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
|
||||
|
||||
// comparison with T
|
||||
template<class T1, class E1, class T2>
|
||||
friend constexpr bool operator==(const expected<T1, E1>&, const T2&);
|
||||
template<class T1, class E1, class T2>
|
||||
friend constexpr bool operator==(const T2&, const expected<T1, E1>&);
|
||||
template<class T1, class E1, class T2>
|
||||
friend constexpr bool operator!=(const expected<T1, E1>&, const T2&);
|
||||
template<class T1, class E1, class T2>
|
||||
friend constexpr bool operator!=(const T2&, const expected<T1, E1>&);
|
||||
|
||||
// Comparison with unexpected<E>
|
||||
template<class T1, class E1, class E2>
|
||||
friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
|
||||
template<class T1, class E1, class E2>
|
||||
friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
|
||||
template<class T1, class E1, class E2>
|
||||
friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
|
||||
template<class T1, class E1, class E2>
|
||||
friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
|
||||
|
||||
// Specialized algorithms
|
||||
template<class T1, class E1>
|
||||
friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
|
||||
|
||||
private:
|
||||
std::variant<value_type, unexpected_type> var_;
|
||||
};
|
||||
|
||||
template<class T1, class E1, class T2, class E2>
|
||||
constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
|
||||
if (x.has_value() != y.has_value()) {
|
||||
return false;
|
||||
} else if (!x.has_value()) {
|
||||
return x.error() == y.error();
|
||||
} else {
|
||||
return *x == *y;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T1, class E1, class T2, class E2>
|
||||
constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
|
||||
if (x.has_value() != y.has_value()) {
|
||||
return true;
|
||||
} else if (!x.has_value()) {
|
||||
return x.error() != y.error();
|
||||
} else {
|
||||
return *x != *y;
|
||||
}
|
||||
}
|
||||
|
||||
// comparison with T
|
||||
template<class T1, class E1, class T2>
|
||||
constexpr bool operator==(const expected<T1, E1>& x, const T2& y) {
|
||||
return x.has_value() && (*x == y);
|
||||
}
|
||||
template<class T1, class E1, class T2>
|
||||
constexpr bool operator==(const T2& x, const expected<T1, E1>& y) {
|
||||
return y.has_value() && (x == *y);
|
||||
}
|
||||
template<class T1, class E1, class T2>
|
||||
constexpr bool operator!=(const expected<T1, E1>& x, const T2& y) {
|
||||
return !x.has_value() || (*x != y);
|
||||
}
|
||||
template<class T1, class E1, class T2>
|
||||
constexpr bool operator!=(const T2& x, const expected<T1, E1>& y) {
|
||||
return !y.has_value() || (x != *y);
|
||||
}
|
||||
|
||||
// Comparison with unexpected<E>
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
|
||||
return !x.has_value() && (x.error() == y.value());
|
||||
}
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
|
||||
return !y.has_value() && (x.value() == y.error());
|
||||
}
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
|
||||
return x.has_value() || (x.error() != y.value());
|
||||
}
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
|
||||
return y.has_value() || (x.value() != y.error());
|
||||
}
|
||||
|
||||
template<class E>
|
||||
class unexpected {
|
||||
public:
|
||||
// constructors
|
||||
constexpr unexpected(const unexpected&) = default;
|
||||
constexpr unexpected(unexpected&&) = default;
|
||||
|
||||
template<class Err = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err>
|
||||
)>
|
||||
constexpr unexpected(Err&& e)
|
||||
: val_(std::forward<Err>(e)) {}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: val_(il, std::forward<Args>(args)...) {}
|
||||
|
||||
template<class Err _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>> &&
|
||||
!std::is_convertible_v<unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>, E> &&
|
||||
std::is_convertible_v<Err, E> /* non-explicit */
|
||||
)>
|
||||
constexpr unexpected(const unexpected<Err>& rhs)
|
||||
: val_(rhs.value()) {}
|
||||
|
||||
template<class Err _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>> &&
|
||||
!std::is_convertible_v<unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<Err, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit unexpected(const unexpected<Err>& rhs)
|
||||
: val_(E(rhs.value())) {}
|
||||
|
||||
template<class Err _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>> &&
|
||||
!std::is_convertible_v<unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>, E> &&
|
||||
std::is_convertible_v<Err, E> /* non-explicit */
|
||||
)>
|
||||
constexpr unexpected(unexpected<Err>&& rhs)
|
||||
: val_(std::move(rhs.value())) {}
|
||||
|
||||
template<class Err _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>> &&
|
||||
!std::is_convertible_v<unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<Err, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit unexpected(unexpected<Err>&& rhs)
|
||||
: val_(E(std::move(rhs.value()))) {}
|
||||
|
||||
// assignment
|
||||
constexpr unexpected& operator=(const unexpected&) = default;
|
||||
constexpr unexpected& operator=(unexpected&&) = default;
|
||||
template<class Err = E>
|
||||
constexpr unexpected& operator=(const unexpected<Err>& rhs) {
|
||||
val_ = rhs.value();
|
||||
return *this;
|
||||
}
|
||||
template<class Err = E>
|
||||
constexpr unexpected& operator=(unexpected<Err>&& rhs) {
|
||||
val_ = std::forward<E>(rhs.value());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// observer
|
||||
constexpr const E& value() const& noexcept { return val_; }
|
||||
constexpr E& value() & noexcept { return val_; }
|
||||
constexpr const E&& value() const&& noexcept { return std::move(val_); }
|
||||
constexpr E&& value() && noexcept { return std::move(val_); }
|
||||
|
||||
void swap(unexpected& other) noexcept { std::swap(val_, other.val_); }
|
||||
|
||||
template<class E1, class E2>
|
||||
friend constexpr bool
|
||||
operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
|
||||
template<class E1, class E2>
|
||||
friend constexpr bool
|
||||
operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
|
||||
|
||||
template<class E1>
|
||||
friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
|
||||
private:
|
||||
E val_;
|
||||
};
|
||||
|
||||
template<class E1, class E2>
|
||||
constexpr bool
|
||||
operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
|
||||
return e1.value() == e2.value();
|
||||
}
|
||||
|
||||
template<class E1, class E2>
|
||||
constexpr bool
|
||||
operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
|
||||
return e1.value() != e2.value();
|
||||
}
|
||||
|
||||
template<class E1>
|
||||
void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
// TODO: bad_expected_access class
|
||||
|
||||
#undef _ENABLE_IF
|
||||
#undef _NODISCARD_
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
Loading…
Reference in New Issue