mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Replace the result<...> implementation by a lighter one
* result<...> only moveable now, the conditional copy capabilities were removed * If you require the optional copy capability use a std::variant implementation of your choice * Closes #13
This commit is contained in:
parent
5f8b2aa317
commit
1e39bd85dd
@ -34,8 +34,8 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/utility/flat-variant.hpp>
|
||||
#include <continuable/detail/utility/result-trait.hpp>
|
||||
#include <continuable/detail/utility/result-variant.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
@ -49,6 +49,11 @@ namespace cti {
|
||||
/// or was cancelled.
|
||||
/// \{
|
||||
|
||||
/// A tag which represents present void values in result.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
using void_arg_t = detail::void_arg_t;
|
||||
|
||||
/// A class which is convertible to any \ref result and that definitely holds no
|
||||
/// value so the real result gets invalidated when this object is passed to it.
|
||||
///
|
||||
@ -137,43 +142,53 @@ public:
|
||||
template <typename... T>
|
||||
class result {
|
||||
using trait_t = detail::result_trait<T...>;
|
||||
using surrogate_t = typename trait_t::surrogate_t;
|
||||
|
||||
template <typename... Args>
|
||||
explicit result(detail::init_arg_t, Args&&... values)
|
||||
: variant_(trait_t::wrap(std::forward<Args>(values)...)) {}
|
||||
explicit result(detail::init_arg_t, exception_t exception)
|
||||
: variant_(std::move(exception)) {}
|
||||
explicit result(detail::init_result_arg_t arg, Args&&... values)
|
||||
: variant_(arg, trait_t::wrap(std::forward<Args>(values)...)) {}
|
||||
explicit result(detail::init_exception_arg_t arg, exception_t exception)
|
||||
: variant_(arg, std::move(exception)) {}
|
||||
|
||||
public:
|
||||
using value_t = typename trait_t::value_t;
|
||||
using value_placeholder_t = typename trait_t::surrogate_t;
|
||||
|
||||
template <typename FirstArg, typename... Args>
|
||||
explicit result(FirstArg&& first, Args&&... values)
|
||||
: variant_(trait_t::wrap(std::forward<FirstArg>(first),
|
||||
: variant_(detail::init_result_arg_t{},
|
||||
trait_t::wrap(std::forward<FirstArg>(first),
|
||||
std::forward<Args>(values)...)) {}
|
||||
|
||||
result() = default;
|
||||
result(result const&) = default;
|
||||
result(result const&) = delete;
|
||||
result(result&&) = default;
|
||||
result& operator=(result const&) = default;
|
||||
result& operator=(result const&) = delete;
|
||||
result& operator=(result&&) = default;
|
||||
~result() = default;
|
||||
|
||||
explicit result(exception_t exception)
|
||||
: variant_(std::move(exception)) {}
|
||||
: variant_(detail::init_exception_arg_t{}, std::move(exception)) {}
|
||||
/* implicit */ result(empty_result) {}
|
||||
/* implicit */ result(exceptional_result exceptional_result)
|
||||
: variant_(std::move(exceptional_result.get_exception())) {}
|
||||
: variant_(detail::init_exception_arg_t{},
|
||||
std::move(exceptional_result.get_exception())) {}
|
||||
/* implicit */ result(cancellation_result)
|
||||
: variant_(exception_t{}) {}
|
||||
: variant_(detail::init_exception_arg_t{}, exception_t{}) {}
|
||||
|
||||
result& operator=(empty_result) {
|
||||
set_empty();
|
||||
variant_.set_empty();
|
||||
return *this;
|
||||
}
|
||||
result& operator=(exceptional_result exceptional_result) {
|
||||
set_exception(std::move(exceptional_result.get_exception()));
|
||||
result& operator=(value_placeholder_t value) {
|
||||
variant_.set_value(std::move(value));
|
||||
return *this;
|
||||
}
|
||||
result& operator=(exceptional_result exception) {
|
||||
variant_.set_exception(std::move(exception.get_exception()));
|
||||
return *this;
|
||||
}
|
||||
result& operator=(cancellation_result) {
|
||||
variant_.set_exception({});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -183,15 +198,15 @@ public:
|
||||
}
|
||||
/// Set the result to a the state which holds the corresponding value
|
||||
void set_value(T... values) {
|
||||
variant_ = trait_t::wrap(std::move(values)...);
|
||||
variant_.set_value(trait_t::wrap(std::move(values)...));
|
||||
}
|
||||
/// Set the result into a state which holds the corresponding exception
|
||||
void set_exception(exception_t exception) {
|
||||
variant_ = std::move(exception);
|
||||
variant_.set_exception(std::move(exception));
|
||||
}
|
||||
/// Set the result into a state which holds the cancellation token
|
||||
void set_canceled() {
|
||||
variant_ = exception_t{};
|
||||
variant_.set_exception(exception_t{});
|
||||
}
|
||||
|
||||
/// Returns true if the state of the result is empty
|
||||
@ -200,11 +215,11 @@ public:
|
||||
}
|
||||
/// Returns true if the state of the result holds the result
|
||||
bool is_value() const noexcept {
|
||||
return variant_.template is<surrogate_t>();
|
||||
return variant_.is_value();
|
||||
}
|
||||
/// Returns true if the state of the result holds a present exception
|
||||
bool is_exception() const noexcept {
|
||||
return variant_.template is<exception_t>();
|
||||
return variant_.is_exception();
|
||||
}
|
||||
|
||||
/// \copydoc is_value
|
||||
@ -215,15 +230,15 @@ public:
|
||||
/// Returns the values of the result, if the result doesn't hold the value
|
||||
/// the behaviour is undefined but will assert in debug mode.
|
||||
decltype(auto) get_value() & noexcept {
|
||||
return trait_t::unwrap(variant_.template cast<surrogate_t>());
|
||||
return trait_t::unwrap(variant_.get_value());
|
||||
}
|
||||
///\copydoc get_value
|
||||
decltype(auto) get_value() const& noexcept {
|
||||
return trait_t::unwrap(variant_.template cast<surrogate_t>());
|
||||
return trait_t::unwrap(variant_.get_value());
|
||||
}
|
||||
///\copydoc get_value
|
||||
decltype(auto) get_value() && noexcept {
|
||||
return trait_t::unwrap(std::move(variant_).template cast<surrogate_t>());
|
||||
return trait_t::unwrap(std::move(variant_.get_value()));
|
||||
}
|
||||
|
||||
///\copydoc get_value
|
||||
@ -236,30 +251,30 @@ public:
|
||||
}
|
||||
///\copydoc get_value
|
||||
decltype(auto) operator*() && noexcept {
|
||||
return std::move(*this).get_value();
|
||||
return std::move(variant_.get_value());
|
||||
}
|
||||
|
||||
/// Returns the exception of the result, if the result doesn't hold an
|
||||
/// exception the behaviour is undefined but will assert in debug mode.
|
||||
exception_t& get_exception() & noexcept {
|
||||
return variant_.template cast<exception_t>();
|
||||
return variant_.get_exception();
|
||||
}
|
||||
/// \copydoc get_exception
|
||||
exception_t const& get_exception() const& noexcept {
|
||||
return variant_.template cast<exception_t>();
|
||||
return variant_.get_exception();
|
||||
}
|
||||
/// \copydoc get_exception
|
||||
exception_t&& get_exception() && noexcept {
|
||||
return std::move(variant_).template cast<exception_t>();
|
||||
return std::move(variant_.get_exception());
|
||||
}
|
||||
|
||||
/// Creates a present result from the given values
|
||||
static result from(T... values) {
|
||||
return result{detail::init_arg_t{}, std::move(values)...};
|
||||
return result{detail::init_result_arg_t{}, std::move(values)...};
|
||||
}
|
||||
/// Creates a present result from the given exception
|
||||
static result from(exception_arg_t, exception_t exception) {
|
||||
return result{detail::init_arg_t{}, std::move(exception)};
|
||||
return result{detail::init_exception_arg_t{}, std::move(exception)};
|
||||
}
|
||||
|
||||
/// Creates an empty result
|
||||
@ -268,7 +283,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
detail::container::flat_variant<surrogate_t, exception_t> variant_;
|
||||
detail::result_variant<value_placeholder_t> variant_;
|
||||
};
|
||||
|
||||
/// Returns the value at position I of the given result
|
||||
|
||||
@ -38,7 +38,6 @@
|
||||
#include <continuable/continuable-result.hpp>
|
||||
#include <continuable/continuable-traverse.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/utility/flat-variant.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
@ -55,22 +54,25 @@ namespace connection {
|
||||
/// - single async value -> single value
|
||||
/// - multiple async value -> tuple of async values.
|
||||
namespace aggregated {
|
||||
|
||||
/// Guards a type to be default constructible,
|
||||
/// and wraps it into an optional type if it isn't default constructible.
|
||||
template <typename T>
|
||||
using lazy_value_t = std::conditional_t<std::is_default_constructible<T>::value,
|
||||
T, container::flat_variant<T>>;
|
||||
T, result<T>>;
|
||||
|
||||
template <typename T>
|
||||
decltype(auto) unpack_lazy(T&& value) {
|
||||
decltype(auto) unpack_lazy(std::true_type /*is_default_constructible*/,
|
||||
T&& value) {
|
||||
return std::forward<T>(value);
|
||||
}
|
||||
template <typename T>
|
||||
T&& unpack_lazy(container::flat_variant<T>&& value) {
|
||||
assert(value.template is<T>() &&
|
||||
T&& unpack_lazy(std::false_type /*is_default_constructible*/,
|
||||
result<T>&& value) {
|
||||
assert(value.is_value() &&
|
||||
"The connection was finalized before all values were present!");
|
||||
|
||||
return std::move(value.template cast<T>());
|
||||
return std::move(value).get_value();
|
||||
}
|
||||
|
||||
template <typename Continuable>
|
||||
@ -82,8 +84,7 @@ class continuable_box<continuable_base<Data, identity<>>> {
|
||||
|
||||
public:
|
||||
explicit continuable_box(continuable_base<Data, identity<>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {
|
||||
}
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
@ -93,13 +94,13 @@ public:
|
||||
return std::move(continuable_);
|
||||
}
|
||||
|
||||
void assign() {
|
||||
}
|
||||
void assign() {}
|
||||
|
||||
auto unbox() && {
|
||||
return spread_this();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Data, typename First>
|
||||
class continuable_box<continuable_base<Data, identity<First>>> {
|
||||
|
||||
@ -109,8 +110,7 @@ class continuable_box<continuable_base<Data, identity<First>>> {
|
||||
public:
|
||||
explicit continuable_box(
|
||||
continuable_base<Data, identity<First>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {
|
||||
}
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
@ -125,7 +125,8 @@ public:
|
||||
}
|
||||
|
||||
auto unbox() && {
|
||||
return unpack_lazy(std::move(first_));
|
||||
return unpack_lazy(std::is_default_constructible<First>{},
|
||||
std::move(first_));
|
||||
}
|
||||
};
|
||||
template <typename Data, typename First, typename Second, typename... Rest>
|
||||
@ -138,8 +139,7 @@ class continuable_box<
|
||||
public:
|
||||
explicit continuable_box(
|
||||
continuable_base<Data, identity<First, Second, Rest...>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {
|
||||
}
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
@ -159,7 +159,9 @@ public:
|
||||
[](auto&&... args) {
|
||||
return spread_this(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
unpack_lazy(std::move(args_)));
|
||||
unpack_lazy(
|
||||
std::is_default_constructible<std::tuple<First, Second, Rest...>>{},
|
||||
std::move(args_)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,379 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.0.0
|
||||
|
||||
Copyright(c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_FLAT_VARIANT_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_FLAT_VARIANT_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace container {
|
||||
namespace detail {
|
||||
// We don't want to pull the algorithm header in
|
||||
constexpr std::size_t max_element_of(std::initializer_list<std::size_t> list) {
|
||||
std::size_t m = 0;
|
||||
for (auto current : list) {
|
||||
if (current > m) {
|
||||
m = current;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/// Workarround for a regression introduced in ~ MSVC 15.8.1
|
||||
template <typename T>
|
||||
using size_of_helper = std::integral_constant<std::size_t, sizeof(T)>;
|
||||
template <typename T>
|
||||
using align_of_helper = std::integral_constant<std::size_t, alignof(T)>;
|
||||
|
||||
template <typename... T>
|
||||
constexpr auto storage_of_impl(identity<T...>) {
|
||||
constexpr auto size = max_element_of({(size_of_helper<T>::value)...});
|
||||
constexpr auto align = max_element_of({(align_of_helper<T>::value)...});
|
||||
return std::aligned_storage_t<size, align>{};
|
||||
}
|
||||
|
||||
/// Declares the aligned storage union for the given types
|
||||
template <typename... T>
|
||||
using storage_of_t = decltype(storage_of_impl(identity<T...>{}));
|
||||
|
||||
/// The value fpr the empty slot
|
||||
using slot_t = std::uint8_t;
|
||||
|
||||
/// The value which is used to mark the empty slot
|
||||
using empty_slot =
|
||||
std::integral_constant<slot_t, std::numeric_limits<slot_t>::max()>;
|
||||
|
||||
template <typename... T>
|
||||
struct flat_variant_base {
|
||||
storage_of_t<T...> storage_;
|
||||
slot_t slot_;
|
||||
|
||||
constexpr flat_variant_base() : slot_(empty_slot::value) {
|
||||
}
|
||||
|
||||
flat_variant_base(flat_variant_base const&) noexcept {
|
||||
}
|
||||
flat_variant_base(flat_variant_base&&) noexcept {
|
||||
}
|
||||
flat_variant_base& operator=(flat_variant_base const&) {
|
||||
return *this;
|
||||
}
|
||||
flat_variant_base& operator=(flat_variant_base&&) {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct flat_variant_move_base {
|
||||
constexpr flat_variant_move_base() = default;
|
||||
|
||||
flat_variant_move_base(flat_variant_move_base const&) = default;
|
||||
explicit flat_variant_move_base(flat_variant_move_base&& right) {
|
||||
Base& me = *static_cast<Base*>(this);
|
||||
Base& other = *static_cast<Base*>(&right);
|
||||
|
||||
if (other.is_empty()) {
|
||||
me.set_slot(empty_slot::value);
|
||||
} else {
|
||||
|
||||
other.visit([&](auto&& value) {
|
||||
#ifndef NDEBUG
|
||||
me.set_slot(empty_slot::value);
|
||||
#endif
|
||||
// NOLINTNEXTLINE(misc-move-forwarding-reference)
|
||||
me.init(std::move(value), other.get_slot());
|
||||
});
|
||||
}
|
||||
|
||||
other.destroy();
|
||||
}
|
||||
flat_variant_move_base& operator=(flat_variant_move_base const&) = default;
|
||||
flat_variant_move_base& operator=(flat_variant_move_base&& right) {
|
||||
Base& me = *static_cast<Base*>(this);
|
||||
Base& other = *static_cast<Base*>(&right);
|
||||
|
||||
me.weak_destroy();
|
||||
|
||||
if (other.is_empty()) {
|
||||
me.set_slot(empty_slot::value);
|
||||
} else {
|
||||
other.visit([&](auto&& value) {
|
||||
// ...
|
||||
me.init(std::move(value), other.get_slot());
|
||||
});
|
||||
}
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
template <typename Base, bool IsCopyable /*= true*/>
|
||||
struct flat_variant_copy_base : flat_variant_move_base<Base> {
|
||||
constexpr flat_variant_copy_base() = default;
|
||||
|
||||
flat_variant_copy_base(flat_variant_copy_base&&) = default;
|
||||
explicit flat_variant_copy_base(flat_variant_copy_base const& right)
|
||||
: flat_variant_move_base<Base>()
|
||||
// TODO noexcept(Base::is_nothrow_move_constructible)
|
||||
{
|
||||
Base& me = *static_cast<Base*>(this);
|
||||
Base const& other = *static_cast<Base const*>(&right);
|
||||
|
||||
if (other.is_empty()) {
|
||||
me.set_slot(empty_slot::value);
|
||||
} else {
|
||||
other.visit([&](auto&& value) {
|
||||
#ifndef NDEBUG
|
||||
me.set_slot(empty_slot::value);
|
||||
#endif
|
||||
me.init(std::move(value), other.get_slot());
|
||||
});
|
||||
}
|
||||
}
|
||||
flat_variant_copy_base& operator=(flat_variant_copy_base&&) = default;
|
||||
flat_variant_copy_base& operator=(flat_variant_copy_base const& right)
|
||||
// TODO noexcept(Base::is_nothrow_move_constructible)
|
||||
{
|
||||
Base& me = *static_cast<Base*>(this);
|
||||
Base const& other = *static_cast<Base const*>(&right);
|
||||
|
||||
me.weak_destroy();
|
||||
|
||||
if (other.is_empty()) {
|
||||
me.set_slot(empty_slot::value);
|
||||
} else {
|
||||
other.visit([&](auto&& value) {
|
||||
// ...
|
||||
me.init(std::move(value), other.get_slot());
|
||||
});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
template <typename Base /*, bool IsCopyable = false*/>
|
||||
struct flat_variant_copy_base<Base, false> : flat_variant_move_base<Base> {
|
||||
constexpr flat_variant_copy_base() = default;
|
||||
|
||||
flat_variant_copy_base(flat_variant_copy_base const&) = delete;
|
||||
explicit flat_variant_copy_base(flat_variant_copy_base&& right) = default;
|
||||
flat_variant_copy_base& operator=(flat_variant_copy_base const&) = delete;
|
||||
flat_variant_copy_base& operator=(flat_variant_copy_base&&) = default;
|
||||
};
|
||||
|
||||
/// Deduces to a true_type if all parameters T satisfy the predicate.
|
||||
template <template <typename> class Predicate, typename... T>
|
||||
using every = traits::conjunction<Predicate<T>...>;
|
||||
} // namespace detail
|
||||
|
||||
/// A class similar to the one in the variant proposal,
|
||||
/// however it is capable of carrying an empty state by default.
|
||||
template <typename... T>
|
||||
class flat_variant;
|
||||
|
||||
template <typename T>
|
||||
struct is_flat_variant : std::false_type {};
|
||||
template <typename... T>
|
||||
struct is_flat_variant<flat_variant<T...>> : std::true_type {};
|
||||
|
||||
template <typename... T>
|
||||
class flat_variant
|
||||
: detail::flat_variant_copy_base<
|
||||
flat_variant<T...>,
|
||||
detail::every<std::is_copy_constructible, T...>::value>,
|
||||
detail::flat_variant_base<T...> {
|
||||
|
||||
static_assert(sizeof...(T) > 0, "At least one paremeter T is required!");
|
||||
|
||||
template <typename...>
|
||||
friend class flat_variant;
|
||||
template <typename>
|
||||
friend struct detail::flat_variant_move_base;
|
||||
template <typename, bool>
|
||||
friend struct detail::flat_variant_copy_base;
|
||||
|
||||
template <typename V>
|
||||
flat_variant(V&& value, detail::slot_t const slot) {
|
||||
#ifndef NDEBUG
|
||||
set_slot(detail::empty_slot::value);
|
||||
#endif
|
||||
init(std::forward<V>(value), slot);
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr flat_variant() = default;
|
||||
flat_variant(flat_variant const&) = default;
|
||||
flat_variant(flat_variant&&) = default;
|
||||
flat_variant& operator=(flat_variant const&) = default;
|
||||
flat_variant& operator=(flat_variant&&) = default;
|
||||
|
||||
~flat_variant() noexcept(
|
||||
detail::every<std::is_nothrow_destructible, T...>::value) {
|
||||
weak_destroy();
|
||||
}
|
||||
|
||||
template <
|
||||
typename V,
|
||||
std::enable_if_t<!is_flat_variant<std::decay_t<V>>::value>* = nullptr>
|
||||
// Since the flat_variant isn't allowed through SFINAE
|
||||
// this overload is safed against the linted issue.
|
||||
// NOLINTNEXTLINE(misc-forwarding-reference-overload)
|
||||
explicit flat_variant(V&& value)
|
||||
: flat_variant(std::forward<V>(value),
|
||||
traits::index_of_t<std::decay_t<V>, T...>::value) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename V,
|
||||
std::enable_if_t<!is_flat_variant<std::decay_t<V>>::value>* = nullptr>
|
||||
flat_variant& operator=(V&& value) {
|
||||
weak_destroy();
|
||||
init(std::forward<V>(value),
|
||||
traits::index_of_t<std::decay_t<V>, T...>::value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_empty() {
|
||||
weak_destroy();
|
||||
set_slot(detail::empty_slot::value);
|
||||
}
|
||||
|
||||
template <typename V, std::size_t Index =
|
||||
traits::index_of_t<std::decay_t<V>, T...>::value>
|
||||
bool is() const noexcept {
|
||||
return is_slot(Index);
|
||||
}
|
||||
|
||||
bool is_empty() const noexcept {
|
||||
return is_slot(detail::empty_slot::value);
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const noexcept {
|
||||
return !is_empty();
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
V& cast() & noexcept {
|
||||
assert(is_slot(traits::index_of_t<std::decay_t<V>, T...>::value));
|
||||
return *reinterpret_cast<std::decay_t<V>*>(&this->storage_);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
V const& cast() const& noexcept {
|
||||
assert(is_slot(traits::index_of_t<std::decay_t<V>, T...>::value));
|
||||
return *reinterpret_cast<std::decay_t<V> const*>(&this->storage_);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
V&& cast() && noexcept {
|
||||
assert(is_slot(traits::index_of_t<std::decay_t<V>, T...>::value));
|
||||
auto& value = *reinterpret_cast<std::decay_t<V>*>(&this->storage_);
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename C, typename V>
|
||||
static void visit_dispatch(flat_variant* me, V&& visitor) {
|
||||
std::forward<V>(visitor)(me->cast<C>());
|
||||
}
|
||||
template <typename C, typename V>
|
||||
static void visit_dispatch_const(flat_variant const* me, V&& visitor) {
|
||||
std::forward<V>(visitor)(me->cast<C>());
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void visit(V&& visitor) {
|
||||
if (!is_empty()) {
|
||||
using callback_t = void (*)(flat_variant*, V &&);
|
||||
constexpr callback_t const callbacks[] = {&visit_dispatch<T, V>...};
|
||||
callbacks[get_slot()](this, std::forward<V>(visitor));
|
||||
}
|
||||
}
|
||||
template <typename V>
|
||||
void visit(V&& visitor) const {
|
||||
if (!is_empty()) {
|
||||
using callback_t = void (*)(flat_variant const*, V&&);
|
||||
constexpr callback_t const callbacks[] = {&visit_dispatch_const<T, V>...};
|
||||
callbacks[get_slot()](this, std::forward<V>(visitor));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void init(V&& value, detail::slot_t const slot) {
|
||||
assert(is_empty());
|
||||
assert(sizeof(this->storage_) >= sizeof(std::decay_t<V>));
|
||||
|
||||
using type = std::decay_t<V>;
|
||||
new (&this->storage_) type(std::forward<V>(value));
|
||||
set_slot(slot);
|
||||
}
|
||||
void destroy() {
|
||||
weak_destroy();
|
||||
|
||||
#ifdef NDEBUG
|
||||
set_slot(detail::empty_slot::value);
|
||||
#endif
|
||||
}
|
||||
void weak_destroy() {
|
||||
visit([&](auto&& value) {
|
||||
using type = std::decay_t<decltype(value)>;
|
||||
value.~type();
|
||||
});
|
||||
|
||||
#ifndef NDEBUG
|
||||
set_slot(detail::empty_slot::value);
|
||||
#endif
|
||||
}
|
||||
detail::slot_t get_slot() const noexcept {
|
||||
// Check for invalid values especially for memory corruption,
|
||||
// the empty element is included.
|
||||
assert((this->slot_ < sizeof...(T)) ||
|
||||
(this->slot_ == detail::empty_slot::value));
|
||||
return this->slot_;
|
||||
}
|
||||
bool is_slot(detail::slot_t const slot) const noexcept {
|
||||
return get_slot() == slot;
|
||||
}
|
||||
void set_slot(detail::slot_t const slot) {
|
||||
this->slot_ = slot;
|
||||
}
|
||||
};
|
||||
} // namespace container
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_FLAT_VARIANT_HPP_INCLUDED
|
||||
@ -40,12 +40,14 @@
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
struct void_arg_t { };
|
||||
|
||||
template <typename... T>
|
||||
struct result_trait;
|
||||
template <>
|
||||
struct result_trait<> {
|
||||
using value_t = void;
|
||||
struct surrogate_t {};
|
||||
using surrogate_t = void_arg_t;
|
||||
|
||||
static constexpr surrogate_t wrap() noexcept {
|
||||
return {};
|
||||
@ -93,8 +95,6 @@ struct result_trait<First, Second, Rest...> {
|
||||
return std::get<I>(std::forward<Result>(result).get_value());
|
||||
}
|
||||
};
|
||||
|
||||
struct init_arg_t {};
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
|
||||
217
include/continuable/detail/utility/result-variant.hpp
Normal file
217
include/continuable/detail/utility/result-variant.hpp
Normal file
@ -0,0 +1,217 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.0.0
|
||||
|
||||
Copyright(c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_RESULT_VARIANT_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_RESULT_VARIANT_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace container {
|
||||
enum class result_slot_t : std::uint8_t {
|
||||
slot_empty,
|
||||
slot_value,
|
||||
slot_exception,
|
||||
};
|
||||
} // namespace container
|
||||
|
||||
struct init_empty_arg_t {};
|
||||
struct init_result_arg_t {};
|
||||
struct init_exception_arg_t {};
|
||||
|
||||
template <typename T>
|
||||
class result_variant {
|
||||
static constexpr bool is_nothrow_destructible = //
|
||||
std::is_nothrow_destructible<T>::value &&
|
||||
std::is_nothrow_destructible<exception_t>::value;
|
||||
static constexpr bool is_nothrow_move_constructible = //
|
||||
std::is_nothrow_move_constructible<T>::value &&
|
||||
std::is_nothrow_move_constructible<exception_t>::value;
|
||||
|
||||
public:
|
||||
result_variant() = default;
|
||||
~result_variant() noexcept(is_nothrow_destructible) {
|
||||
destroy();
|
||||
}
|
||||
|
||||
explicit result_variant(init_empty_arg_t) noexcept
|
||||
: slot_(container::result_slot_t::slot_empty) {}
|
||||
explicit result_variant(init_result_arg_t, T value) noexcept(
|
||||
std::is_nothrow_destructible<T>::value&&
|
||||
std::is_nothrow_move_constructible<T>::value)
|
||||
: slot_(container::result_slot_t::slot_value) {
|
||||
new (value_ptr()) T(std::move(value));
|
||||
}
|
||||
explicit result_variant(init_exception_arg_t, exception_t exception) noexcept(
|
||||
std::is_nothrow_destructible<exception_t>::value&&
|
||||
std::is_nothrow_move_constructible<exception_t>::value)
|
||||
: slot_(container::result_slot_t::slot_exception) {
|
||||
new (exception_ptr()) exception_t(std::move(exception));
|
||||
}
|
||||
|
||||
result_variant(result_variant const&) = delete;
|
||||
result_variant& operator=(result_variant const&) = delete;
|
||||
|
||||
result_variant(result_variant&& other) noexcept(
|
||||
is_nothrow_destructible&& is_nothrow_move_constructible)
|
||||
: slot_(other.slot_) {
|
||||
|
||||
switch (other.slot_) {
|
||||
case container::result_slot_t::slot_value: {
|
||||
new (value_ptr()) T(std::move(*other.value_ptr()));
|
||||
break;
|
||||
}
|
||||
case container::result_slot_t::slot_exception: {
|
||||
new (exception_ptr()) exception_t(std::move(*other.exception_ptr()));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
other.destroy();
|
||||
other.slot_ = container::result_slot_t::slot_empty;
|
||||
}
|
||||
|
||||
result_variant& operator=(result_variant&& other) noexcept(
|
||||
is_nothrow_destructible&& is_nothrow_move_constructible) {
|
||||
|
||||
destroy();
|
||||
slot_ = other.slot_;
|
||||
|
||||
switch (other.slot_) {
|
||||
case container::result_slot_t::slot_value: {
|
||||
new (value_ptr()) T(std::move(*other.value_ptr()));
|
||||
break;
|
||||
}
|
||||
case container::result_slot_t::slot_exception: {
|
||||
new (exception_ptr()) exception_t(std::move(*other.exception_ptr()));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
other.destroy();
|
||||
other.slot_ = container::result_slot_t::slot_empty;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_empty() {
|
||||
destroy();
|
||||
slot_ = container::result_slot_t::slot_empty;
|
||||
}
|
||||
void set_value(T value) {
|
||||
destroy();
|
||||
new (value_ptr()) T(std::move(value));
|
||||
slot_ = container::result_slot_t::slot_value;
|
||||
}
|
||||
void set_exception(exception_t exception) {
|
||||
destroy();
|
||||
new (exception_ptr()) exception_t(std::move(exception));
|
||||
slot_ = container::result_slot_t::slot_exception;
|
||||
}
|
||||
|
||||
container::result_slot_t slot() const noexcept {
|
||||
return slot_;
|
||||
}
|
||||
|
||||
bool is_empty() const noexcept {
|
||||
return slot_ == container::result_slot_t::slot_empty;
|
||||
}
|
||||
bool is_value() const noexcept {
|
||||
return slot_ == container::result_slot_t::slot_value;
|
||||
}
|
||||
bool is_exception() const noexcept {
|
||||
return slot_ == container::result_slot_t::slot_exception;
|
||||
}
|
||||
|
||||
T& get_value() noexcept {
|
||||
assert(is_value());
|
||||
return *reinterpret_cast<T*>(&storage_);
|
||||
}
|
||||
T const& get_value() const noexcept {
|
||||
assert(is_value());
|
||||
return *reinterpret_cast<T const*>(&storage_);
|
||||
}
|
||||
|
||||
exception_t& get_exception() noexcept {
|
||||
assert(is_exception());
|
||||
return *reinterpret_cast<exception_t*>(&storage_);
|
||||
}
|
||||
exception_t const& get_exception() const noexcept {
|
||||
assert(is_exception());
|
||||
return *reinterpret_cast<exception_t const*>(&storage_);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr T* value_ptr() noexcept {
|
||||
return reinterpret_cast<T*>(&storage_);
|
||||
}
|
||||
constexpr exception_t* exception_ptr() noexcept {
|
||||
return reinterpret_cast<exception_t*>(&storage_);
|
||||
}
|
||||
|
||||
void destroy() noexcept(is_nothrow_destructible) {
|
||||
switch (slot_) {
|
||||
case container::result_slot_t::slot_value: {
|
||||
value_ptr()->~T();
|
||||
break;
|
||||
}
|
||||
case container::result_slot_t::slot_exception: {
|
||||
exception_ptr()->~exception_t();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
container::result_slot_t slot_{container::result_slot_t::slot_empty};
|
||||
std::aligned_storage_t<
|
||||
(sizeof(T) > sizeof(exception_t) ? sizeof(T) : sizeof(exception_t)),
|
||||
(alignof(T) > alignof(exception_t) ? alignof(T) : alignof(exception_t))>
|
||||
storage_;
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_RESULT_VARIANT_HPP_INCLUDED
|
||||
@ -18,7 +18,6 @@ add_executable(test-continuable-single
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-promise.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-connection-noinst
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-forward-decl.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-flat-variant.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-result.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-ready.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-promisify.cpp
|
||||
|
||||
@ -1,161 +0,0 @@
|
||||
|
||||
/*
|
||||
Copyright(c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
**/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <continuable/detail/utility/flat-variant.hpp>
|
||||
#include <test-continuable.hpp>
|
||||
|
||||
using cti::detail::container::flat_variant;
|
||||
|
||||
static int const CANARY = 373671;
|
||||
|
||||
using int_variant = flat_variant<int, tag1, tag2>;
|
||||
|
||||
TEST(flat_variant_single_test, is_move_constructible) {
|
||||
{
|
||||
int_variant e_old(CANARY);
|
||||
int_variant e(std::move(e_old));
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_TRUE(e.is<int>());
|
||||
EXPECT_EQ(e.cast<int>(), CANARY);
|
||||
}
|
||||
|
||||
{
|
||||
int_variant e(int_variant(tag1{}));
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_TRUE(e.is<tag1>());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(flat_variant_single_test, is_value_move_assignable) {
|
||||
int_variant old(CANARY);
|
||||
int_variant e;
|
||||
e = std::move(old);
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_TRUE(e.is<int>());
|
||||
EXPECT_EQ(e.cast<int>(), CANARY);
|
||||
}
|
||||
|
||||
TEST(flat_variant_single_test, is_copy_constructible) {
|
||||
{
|
||||
int_variant const e_old(CANARY);
|
||||
int_variant e(e_old);
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_TRUE(e.is<int>());
|
||||
EXPECT_EQ(e.cast<int>(), CANARY);
|
||||
}
|
||||
|
||||
{
|
||||
int_variant const e_old(tag1{});
|
||||
int_variant e(e_old);
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_TRUE(e.is<tag1>());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(flat_variant_single_test, is_copy_assignable) {
|
||||
{
|
||||
int_variant const e_old(CANARY);
|
||||
int_variant e;
|
||||
e = e_old;
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_TRUE(e.is<int>());
|
||||
EXPECT_EQ(e.cast<int>(), CANARY);
|
||||
}
|
||||
|
||||
{
|
||||
int_variant const e_old(tag1{});
|
||||
int_variant e;
|
||||
e = e_old;
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_TRUE(e.is<tag1>());
|
||||
}
|
||||
}
|
||||
|
||||
// This regression test shows a memory leak which happens when using the
|
||||
// expected class move constructed from another expected object.
|
||||
TEST(flat_variant_single_test, test_leak_regression) {
|
||||
// expected_all_tests<cti::detail::util::expected<std::__1::unique_ptr<int,
|
||||
// std::__1::default_delete<int> > > >::supply<int const&>(int const&)
|
||||
// const
|
||||
// continuable/build/../test/unit-test/test-continuable-expected.cpp:52
|
||||
// 3: #3 0x11cf07a in
|
||||
// expected_all_tests_is_value_assignable_Test<cti::detail::util::expected<std::__1::unique_ptr<int,
|
||||
// std::__1::default_delete<int> > > >::TestBody()
|
||||
// continuable/build/../test/unit-test/test-continuable-expected.cpp:133:15
|
||||
// 3: #4 0x1339e4e in void
|
||||
// testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test,
|
||||
// void>(testing::Test*, void (testing::Test::*)(), char const*)
|
||||
// continuable/build/../dep/googletest/googletest/googletest/src/gtest.cc:2395:10
|
||||
using type = std::shared_ptr<int>;
|
||||
|
||||
auto const validate = [](auto&& variant, long use) {
|
||||
ASSERT_TRUE(variant.template is<type>());
|
||||
ASSERT_EQ((*variant.template cast<type>()), CANARY);
|
||||
ASSERT_EQ(variant.template cast<type>().use_count(), use);
|
||||
};
|
||||
|
||||
bool destroyed = false;
|
||||
{
|
||||
type ptr(new int(CANARY), [&](int* val) {
|
||||
destroyed = true;
|
||||
delete val;
|
||||
});
|
||||
|
||||
auto e1(flat_variant<type>(std::move(ptr)));
|
||||
validate(e1, 1);
|
||||
|
||||
auto e2 = std::move(e1);
|
||||
validate(e2, 1);
|
||||
|
||||
flat_variant<type> e3;
|
||||
e3 = std::move(e2);
|
||||
validate(e3, 1);
|
||||
|
||||
{
|
||||
flat_variant<type> ec(e3);
|
||||
validate(ec, 2);
|
||||
}
|
||||
|
||||
{
|
||||
flat_variant<type> ec = e3;
|
||||
validate(ec, 2);
|
||||
}
|
||||
|
||||
{
|
||||
flat_variant<type> ec;
|
||||
ec = e3;
|
||||
validate(ec, 2);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_TRUE(destroyed);
|
||||
}
|
||||
@ -144,50 +144,6 @@ TYPED_TEST(result_all_tests, is_error_move_assignable) {
|
||||
EXPECT_TRUE(e.is_exception());
|
||||
}
|
||||
|
||||
TEST(result_copyable_tests, is_copy_constructible) {
|
||||
{
|
||||
copyable_type const e_old(CANARY);
|
||||
copyable_type e(e_old);
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_EQ(*e, CANARY);
|
||||
EXPECT_TRUE(e.is_value());
|
||||
EXPECT_FALSE(e.is_exception());
|
||||
}
|
||||
|
||||
{
|
||||
copyable_type const e_old(supply_test_exception());
|
||||
copyable_type e(e_old);
|
||||
|
||||
EXPECT_FALSE(bool(e));
|
||||
EXPECT_FALSE(e.is_value());
|
||||
EXPECT_TRUE(e.is_exception());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(result_copyable_tests, is_copy_assignable) {
|
||||
{
|
||||
copyable_type const e_old(CANARY);
|
||||
copyable_type e;
|
||||
e = e_old;
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_EQ(*e, CANARY);
|
||||
EXPECT_TRUE(e.is_value());
|
||||
EXPECT_FALSE(e.is_exception());
|
||||
}
|
||||
|
||||
{
|
||||
copyable_type const e_old(supply_test_exception());
|
||||
copyable_type e;
|
||||
e = e_old;
|
||||
|
||||
EXPECT_FALSE(bool(e));
|
||||
EXPECT_FALSE(e.is_value());
|
||||
EXPECT_TRUE(e.is_exception());
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(result_all_tests, is_constructible_from_error_helper) {
|
||||
cti::exceptional_result e1(supply_test_exception());
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user