// Copyright (C) 2018 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.
|
|
#ifndef IORAP_SRC_COMMON_EXPECTED_H_
|
#define IORAP_SRC_COMMON_EXPECTED_H_
|
|
#include <type_traits>
|
#include <utility>
|
|
#include <android-base/logging.h> // CHECK/DCHECK.
|
|
// Ignore the tautological-undefined-compare warning.
|
// We obviously want to do this to protect against undefined behavior
|
// that sets a reference to a null value.
|
#define DCHECK_UB_NOT_NULL(x) \
|
DCHECK(reinterpret_cast<volatile decltype(x)>(x) != nullptr)
|
|
/**
|
* Result<Value, Error>-like interface.
|
*
|
* Subset of the experimental standard C++ proposal (p0323r3)
|
*
|
* Example:
|
*
|
* expected<std::string, status_t> x = function_which_might_fail();
|
* if (x) {
|
* std::string str = x.value();
|
* } else {
|
* status_t err = x.error();
|
* }
|
*/
|
|
namespace iorap {
|
namespace detail {
|
// Use perfect forwarding for expected_data constructors with overloading.
|
struct expected_tag{};
|
struct expected_tag_right : public expected_tag {
|
static constexpr bool is_right_v = true;
|
};
|
struct expected_tag_error : public expected_tag {
|
static constexpr bool is_right_v = false;
|
};
|
|
template <typename T, typename E, bool DefineDestructor>
|
struct expected_data;
|
|
// This doesn't always work because this code could be instantiated with a non-trivial T/E,
|
// and then the union becomes invalid.
|
template <typename T, typename E>
|
struct expected_data<T, E, /*DefineDestructor*/true> {
|
// Mark everything 'constexpr' to keep the code the same as the other partial specialization.
|
|
template <typename U>
|
constexpr expected_data(U&& either, expected_tag_right)
|
: right_{std::forward<U>(either)}, is_right_{true} {}
|
|
template <typename U>
|
constexpr expected_data(U&& either, expected_tag_error)
|
: error_{std::forward<U>(either)}, is_right_{false} {}
|
|
constexpr bool has_value() const {
|
return is_right_;
|
}
|
|
constexpr const T& value() const {
|
return right_;
|
}
|
|
constexpr T& value() {
|
return right_;
|
}
|
|
constexpr const E& error() const {
|
return error_;
|
}
|
|
constexpr E& error() {
|
return error_;
|
}
|
|
// Using an "anonymous union" here allows non-trivial types to be stored.
|
union {
|
T right_;
|
E error_;
|
};
|
|
bool is_right_;
|
|
// Below code differs slightly by handling non-trivial constructors/destructors.
|
bool moved_from_{false};
|
|
// Note: Destructors cannot be templated, so it is illegal to use SFINAE to try to
|
// conditionalize this destructor somehow.
|
~expected_data() {
|
if (moved_from_) { return; }
|
if (is_right_) {
|
right_.~T();
|
} else {
|
error_.~E();
|
}
|
}
|
|
expected_data(expected_data&& other)
|
noexcept(
|
noexcept(T(std::move(other.right_))) &&
|
noexcept(E(std::move(other.error_)))
|
) {
|
DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
|
DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
|
if (other.is_right_) {
|
new (&right_) T(std::move(other.right_));
|
} else {
|
new (&error_) E(std::move(other.error_));
|
}
|
other.moved_from_ = true;
|
is_right_ = other.is_right_;
|
}
|
|
expected_data(const expected_data& other) {
|
DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
|
DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
|
if (other.is_right_) {
|
new (&right_) T(other.right_);
|
} else {
|
new (&error_) E(other.error_);
|
}
|
is_right_ = other.is_right_;
|
}
|
|
expected_data& operator=(const expected_data& other) {
|
DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
|
DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
|
|
if (this == &other) {
|
return *this;
|
}
|
|
if (other.is_right_) {
|
if (!is_right_) {
|
error_.~E();
|
new (&right_) T(other.right_);
|
} else {
|
right_ = other.right_;
|
}
|
} else {
|
if (is_right_) {
|
right_.~T();
|
new (&error_) E(other.error_);
|
} else {
|
error_ = other.error_;
|
}
|
}
|
is_right_ = other.is_right_;
|
|
return *this;
|
}
|
|
expected_data& operator=(expected_data&& other) {
|
DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
|
DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
|
|
if (this == &other) {
|
return *this;
|
}
|
|
if (other.is_right_) {
|
if (!is_right_) {
|
error_.~E();
|
new (&right_) T(std::move(other.right_));
|
} else {
|
right_ = std::move(other.right_);
|
}
|
} else {
|
if (is_right_) {
|
right_.~T();
|
new (&error_) E(std::move(other.error_));
|
} else {
|
error_ = std::move(other.error_);
|
}
|
}
|
|
other.moved_from_ = true;
|
is_right_ = other.is_right_;
|
|
return *this;
|
}
|
};
|
|
// Trivial-destructor copy of the above struct.
|
//
|
// A separate copy is required because otherwise compilation fails with an error about
|
// the union having an implicitly deleted constructor.
|
//
|
// Having this implementation gives us the property that
|
//
|
// (is_trivially_destructible<T> && is_trivially_destructible<E>
|
// ==> is_trivially_destructible<expected<T, E>>)
|
template <typename T, typename E>
|
struct expected_data<T, E, /*DefineDestructor*/false> {
|
template <typename U>
|
constexpr expected_data(U&& either, expected_tag_right)
|
: right_{std::forward<U>(either)}, is_right_{true} {}
|
|
template <typename U>
|
constexpr expected_data(U&& either, expected_tag_error)
|
: error_{std::forward<U>(either)}, is_right_{false} {}
|
|
constexpr bool has_value() const {
|
return is_right_;
|
}
|
|
constexpr const T& value() const {
|
return right_;
|
}
|
|
constexpr T& value() {
|
return right_;
|
}
|
|
constexpr const E& error() const {
|
return error_;
|
}
|
|
constexpr E& error() {
|
return error_;
|
}
|
|
// Using an "anonymous union" here allows non-trivial types to be stored.
|
union {
|
T right_;
|
E error_;
|
};
|
|
bool is_right_;
|
|
~expected_data() = default;
|
};
|
|
// Select between trivial and non-trivial implementations. Trivial implementations
|
// are more optimized and constexpr-compatible.
|
template <typename T, typename E>
|
using expected_pick_data_t =
|
expected_data<T, E,
|
!(std::is_trivially_destructible_v<T> && std::is_trivially_destructible_v<E>) >;
|
} // namespace detail
|
|
template <typename E>
|
struct unexpected;
|
|
// Subset of std::experimental::expected proposal (p0323r3).
|
template <typename T, typename E>
|
struct expected {
|
// Never-empty: expected<T,E> values have either 'T' or 'E' in them.
|
template <typename U = T, typename _ = std::enable_if_t<std::is_default_constructible_v<U>>>
|
constexpr expected() noexcept(noexcept(T{})) : expected(T{}) {}
|
|
constexpr expected(const T& value) : data_{value, detail::expected_tag_right{}} {}
|
constexpr expected(T&& value) : data_{std::move(value), detail::expected_tag_right{}} {}
|
constexpr expected(const E& error) : data_{error, detail::expected_tag_error{}} {}
|
constexpr expected(E&& error) : data_{std::move(error), detail::expected_tag_error{}} {}
|
|
template <typename G = E>
|
constexpr expected(unexpected<G> const& u) : expected{u.value()} {}
|
|
template <typename G = E>
|
constexpr expected(unexpected<G>&& u) : expected{std::move(u.value())} {}
|
|
explicit constexpr operator bool() const {
|
return has_value();
|
}
|
|
constexpr bool has_value() const {
|
return data_.has_value();
|
}
|
|
constexpr const T& operator*() const {
|
return data_.value();
|
}
|
|
constexpr T& operator*() {
|
return data_.value();
|
}
|
|
// TODO: arrow operator?
|
|
constexpr T& value() & {
|
CHECK(has_value());
|
return data_.value();
|
}
|
|
constexpr const T& value() const & {
|
CHECK(has_value());
|
return data_.value();
|
}
|
|
constexpr T&& value() && {
|
CHECK(has_value());
|
return std::move(data_.value());
|
}
|
|
constexpr const T& value() const && {
|
CHECK(has_value());
|
return std::move(data_.value());
|
}
|
|
constexpr E& error() {
|
DCHECK(!has_value());
|
return data_.error();
|
}
|
|
constexpr const E& error() const {
|
DCHECK(!has_value());
|
return data_.error();
|
}
|
|
// TODO: other functions such as operator=, unexpected, etc.
|
private:
|
detail::expected_pick_data_t<T, E> data_;
|
};
|
|
// TODO: move to tests file
|
namespace {
|
struct TestType {
|
TestType() {}
|
~TestType() {}
|
};
|
struct TestType2 : TestType {};
|
|
static_assert(std::is_trivially_destructible_v<expected<int, /*error*/double> >);
|
static_assert(!std::is_trivially_destructible_v<expected<TestType, /*error*/double> >);
|
static_assert(!std::is_trivially_destructible_v<expected<int, /*error*/TestType> >);
|
static_assert(!std::is_trivially_destructible_v<expected<TestType, /*error*/TestType2> >);
|
|
// Ensure expected is constexpr-compatible.
|
struct TestCase {
|
static constexpr auto t1 = expected<int, double>{};
|
};
|
} // namespace <anonymous>
|
|
template <typename E>
|
struct unexpected {
|
unexpected() = delete;
|
constexpr explicit unexpected(const E& error) : error_{error} {}
|
constexpr explicit unexpected(E&& error) : error_{std::move(error)} {}
|
constexpr const E& value() const& { return error_; }
|
constexpr E& value() & { return error_; }
|
constexpr E&& value() && { return std::move(error_); }
|
constexpr E const&& value() const&& { return std::move(error_); }
|
private:
|
E error_;
|
};
|
|
template <class E>
|
constexpr bool operator==(const unexpected<E>& x, const unexpected<E>& y) {
|
return x.value() == y.value();
|
}
|
|
template <class E>
|
constexpr bool operator!=(const unexpected<E>& x, const unexpected<E>& y) {
|
return !(x == y);
|
}
|
|
// TODO: move below codes to separate utils file
|
//
|
// future C++20 implementation of std::identity
|
struct identity {
|
template <typename U>
|
constexpr auto operator()(U&& v) const noexcept {
|
return std::forward<U>(v);
|
}
|
};
|
|
// Given a lambda [...](auto&& var) {...}
|
// apply std::forward to 'var' to achieve perfect forwarding.
|
//
|
// Note that this doesn't work when var is a template type, i.e.
|
// template <typename T>
|
// void func(T&& tvar) {...}
|
//
|
// It would be invalid to use this macro with 'tvar' in that context.
|
#define IORAP_FORWARD_LAMBDA(var) std::forward<decltype(var)>(var)
|
|
// Borrowed non-null pointer, i.e. we do not own the lifetime.
|
//
|
// Function calls: This pointer is not used past the call.
|
// Struct fields: This pointer is not used past the lifetime of the struct.
|
template <class T, class = std::enable_if_t<std::is_pointer<T>::value>>
|
using borrowed = T _Nonnull;
|
// TODO: need a DCHECK or high warning levels, since null is technically well-defined.
|
|
} // namespace iorap
|
|
#endif // IORAP_SRC_COMMON_EXPECTED_H_
|