mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
add: [imp] expected (TBD)
This commit is contained in:
parent
4624610e45
commit
18e8a79242
248
include/libimp/expected.h
Normal file
248
include/libimp/expected.h
Normal file
@ -0,0 +1,248 @@
|
||||
/**
|
||||
* \file libimp/expected.h
|
||||
* \author mutouyun (orz@orzz.org)
|
||||
* \brief Provides a way to store either of two values.
|
||||
* \date 2023-02-05
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility> // std::exchange
|
||||
#include <array>
|
||||
#include <memory> // std::addressof
|
||||
#include <cstddef> // std::nullptr_t
|
||||
|
||||
#include "libimp/def.h"
|
||||
#include "libimp/construct.h"
|
||||
#include "libimp/generic.h"
|
||||
#include "libimp/byte.h"
|
||||
|
||||
LIBIMP_NAMESPACE_BEG_
|
||||
|
||||
/**
|
||||
* \brief In-place construction tag for unexpected value in expected.
|
||||
* \see https://en.cppreference.com/w/cpp/utility/expected/unexpect_t
|
||||
*/
|
||||
struct unexpected_t {
|
||||
explicit unexpected_t() = default;
|
||||
};
|
||||
constexpr unexpected_t unexpected {};
|
||||
|
||||
/**
|
||||
* \class template <typename T, typename E> expected
|
||||
* \brief Provides a way to store either of two values.
|
||||
* \see https://en.cppreference.com/w/cpp/utility/expected
|
||||
* \tparam T - the type of the expected value.
|
||||
* \tparam E - the type of the unexpected value.
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
class expected;
|
||||
|
||||
namespace detail_expected {
|
||||
|
||||
template <typename T, typename E>
|
||||
struct data_union {
|
||||
union {
|
||||
T value_; ///< the expected value
|
||||
E error_; ///< the unexpected value
|
||||
};
|
||||
|
||||
data_union(data_union const &) = delete;
|
||||
data_union &operator=(data_union const &) = delete;
|
||||
|
||||
data_union(std::nullptr_t) noexcept {}
|
||||
~data_union() {}
|
||||
|
||||
template <typename... A>
|
||||
data_union(in_place_t, A &&...args) : value_{std::forward<A>(args)...} {}
|
||||
template <typename... A>
|
||||
data_union(unexpected_t, A &&...args) : error_{std::forward<A>(args)...} {}
|
||||
|
||||
void destruct_value() noexcept { destroy(&value_); }
|
||||
void destruct_error() noexcept { destroy(&error_); }
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
struct data_union<void, E> {
|
||||
alignas(E) std::array<byte, sizeof(E)> error_; ///< the unexpected value
|
||||
|
||||
data_union(data_union const &) = delete;
|
||||
data_union &operator=(data_union const &) = delete;
|
||||
|
||||
data_union(std::nullptr_t) noexcept {}
|
||||
|
||||
template <typename... A>
|
||||
data_union(in_place_t, A &&...) noexcept {}
|
||||
template <typename... A>
|
||||
data_union(unexpected_t, A &&...args) {
|
||||
construct<E>(&error_, std::forward<A>(args)...);
|
||||
}
|
||||
|
||||
void destruct_value() noexcept {}
|
||||
void destruct_error() noexcept { destroy(reinterpret_cast<E *>(&error_)); }
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
auto destruct(bool /*has_value*/, data_union<T, E> &/*data*/) noexcept
|
||||
-> typename std::enable_if<std::is_trivially_destructible<T>::value &&
|
||||
std::is_trivially_destructible<E>::value>::type {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
auto destruct(bool has_value, data_union<T, E> &data) noexcept
|
||||
-> typename std::enable_if<!std::is_trivially_destructible<T>::value &&
|
||||
std::is_trivially_destructible<E>::value>::type {
|
||||
if (has_value) data.destruct_value();
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
auto destruct(bool has_value, data_union<T, E> &data) noexcept
|
||||
-> typename std::enable_if< std::is_trivially_destructible<T>::value &&
|
||||
!std::is_trivially_destructible<E>::value>::type {
|
||||
if (!has_value) data.destruct_error();
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
auto destruct(bool has_value, data_union<T, E> &data) noexcept
|
||||
-> typename std::enable_if<!std::is_trivially_destructible<T>::value &&
|
||||
!std::is_trivially_destructible<E>::value>::type {
|
||||
if (has_value) {
|
||||
data.destruct_value();
|
||||
} else {
|
||||
data.destruct_error();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename E>
|
||||
struct value_getter : data_union<T, E> {
|
||||
using data_union<T, E>::data_union;
|
||||
|
||||
value_getter(S const &other) : data_union<T, E>(nullptr) {
|
||||
if (bool(static_cast<S &>(other))) {
|
||||
construct<data_union<T, E>>(this, in_place, other.value_);
|
||||
} else {
|
||||
construct<data_union<T, E>>(this, unexpected, other.error_);
|
||||
}
|
||||
}
|
||||
|
||||
value_getter(S &&other) : data_union<T, E>(nullptr) {
|
||||
if (bool(static_cast<S &>(other))) {
|
||||
construct<data_union<T, E>>(this, in_place, std::move(other.value_));
|
||||
} else {
|
||||
construct<data_union<T, E>>(this, unexpected, std::move(other.error_));
|
||||
}
|
||||
}
|
||||
|
||||
T const & value() const & noexcept { return this->value_; }
|
||||
T & value() & noexcept { return this->value_; }
|
||||
T const &&value() const && noexcept { return std::move(this->value_); }
|
||||
T && value() && noexcept { return std::move(this->value_); }
|
||||
|
||||
T const *operator->() const noexcept { return std::addressof(this->value_); }
|
||||
T * operator->() noexcept { return std::addressof(this->value_); }
|
||||
|
||||
T const & operator*() const & noexcept { return this->value_; }
|
||||
T & operator*() & noexcept { return this->value_; }
|
||||
T const &&operator*() const && noexcept { return std::move(this->value_); }
|
||||
T && operator*() && noexcept { return std::move(this->value_); }
|
||||
|
||||
template <typename U>
|
||||
T value_or(U &&def) const & {
|
||||
return bool(*static_cast<S *>(this)) ? **this : static_cast<T>(std::forward<U>(def));
|
||||
}
|
||||
template <typename U>
|
||||
T value_or(U &&def) && {
|
||||
return bool(*static_cast<S *>(this)) ? std::move(**this) : static_cast<T>(std::forward<U>(def));
|
||||
}
|
||||
|
||||
template <typename... A>
|
||||
T &emplace(A &&...args) {
|
||||
static_cast<S *>(this)->reconstruct(in_place, std::forward<A>(args)...);
|
||||
return this->value_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename S, typename E>
|
||||
struct value_getter<S, void, E> : data_union<void, E> {
|
||||
using data_union<void, E>::data_union;
|
||||
|
||||
value_getter(S const &other) : data_union<T, E>(nullptr) {
|
||||
if (bool(static_cast<S &>(other))) {
|
||||
construct<data_union<T, E>>(this, in_place);
|
||||
} else {
|
||||
construct<data_union<T, E>>(this, unexpected, other.error_);
|
||||
}
|
||||
}
|
||||
|
||||
value_getter(S &&other) : data_union<T, E>(nullptr) {
|
||||
if (bool(static_cast<S &>(other))) {
|
||||
construct<data_union<T, E>>(this, in_place);
|
||||
} else {
|
||||
construct<data_union<T, E>>(this, unexpected, std::move(other.error_));
|
||||
}
|
||||
}
|
||||
|
||||
void emplace() noexcept {
|
||||
static_cast<S *>(this)->reconstruct(in_place);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct storage : value_getter<storage<T, E>, T, E> {
|
||||
using getter_t = value_getter<storage<T, E>, T, E>;
|
||||
|
||||
bool has_value_;
|
||||
|
||||
template <typename... A>
|
||||
storage(in_place_t, A &&...args)
|
||||
: getter_t(in_place, std::forward<A>(args)...)
|
||||
, has_value_(true) {}
|
||||
|
||||
template <typename... A>
|
||||
storage(unexpected_t, A &&...args)
|
||||
: getter_t(unexpected, std::forward<A>(args)...)
|
||||
, has_value_(false) {}
|
||||
|
||||
storage(storage const &other)
|
||||
: getter_t(other)
|
||||
, has_value_(other.has_value_) {}
|
||||
|
||||
storage(storage &&other)
|
||||
: getter_t(std::move(other))
|
||||
, has_value_(std::exchange(other.has_value_, false)) {}
|
||||
|
||||
bool has_value() const noexcept {
|
||||
return has_value_;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return this->has_value();
|
||||
}
|
||||
|
||||
typename std::add_const<E>::type &error() const & noexcept {
|
||||
return this->error_;
|
||||
}
|
||||
E &error() & noexcept {
|
||||
return this->error_;
|
||||
}
|
||||
typename std::add_const<E>::type &&error() const && noexcept {
|
||||
return std::move(this->error_);
|
||||
}
|
||||
E &&error() && noexcept {
|
||||
return std::move(this->error_);
|
||||
}
|
||||
|
||||
protected:
|
||||
friend getter_t;
|
||||
|
||||
template <typename... A>
|
||||
void reconstruct(A &&...args) {
|
||||
destroy(this);
|
||||
construct<storage>(this, std::forward<A>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail_expected
|
||||
|
||||
LIBIMP_NAMESPACE_END_
|
||||
@ -10,20 +10,35 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "libimp/def.h"
|
||||
#include "libimp/detect_plat.h"
|
||||
|
||||
LIBIMP_NAMESPACE_BEG_
|
||||
|
||||
/**
|
||||
* \brief Utility metafunction that maps a sequence of any types to the type void
|
||||
* \see https://en.cppreference.com/w/cpp/types/void_t
|
||||
*/
|
||||
*/
|
||||
template <typename...>
|
||||
using void_t = void;
|
||||
|
||||
/**
|
||||
* \brief To indicate that the contained object should be constructed in-place.
|
||||
* \see https://en.cppreference.com/w/cpp/utility/in_place
|
||||
*/
|
||||
#if defined(LIBIMP_CPP_17)
|
||||
using std::in_place_t;
|
||||
using std::in_place;
|
||||
#else /*!LIBIMP_CPP_17*/
|
||||
struct in_place_t {
|
||||
explicit in_place_t() = default;
|
||||
};
|
||||
constexpr in_place_t in_place {};
|
||||
#endif/*!LIBIMP_CPP_17*/
|
||||
|
||||
/**
|
||||
* \brief A general pattern for supporting customisable functions
|
||||
* \see https://www.open-std.org/jtc1/sc22/WG21/docs/papers/2019/p1895r0.pdf
|
||||
*/
|
||||
*/
|
||||
namespace detail {
|
||||
|
||||
void tag_invoke();
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "libimp/countof.h"
|
||||
#include "libimp/horrible_cast.h"
|
||||
#include "libimp/detect_plat.h"
|
||||
#include "libimp/generic.h"
|
||||
|
||||
TEST(utility, construct) {
|
||||
struct Foo {
|
||||
@ -124,4 +125,12 @@ TEST(utility, horrible_cast) {
|
||||
#endif
|
||||
|
||||
// imp::horrible_cast<std::uint32_t>(0ll);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(LIBIMP_CPP_17)
|
||||
TEST(utility, in_place) {
|
||||
EXPECT_TRUE((std::is_same<std::in_place_t, imp::in_place_t>::value));
|
||||
[](imp::in_place_t) {}(std::in_place);
|
||||
[](std::in_place_t) {}(imp::in_place);
|
||||
}
|
||||
#endif/*LIBIMP_CPP_17*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user