Some more work on the optional variant type

This commit is contained in:
Denis Blank 2018-03-08 14:06:30 +01:00
parent ca03c52d40
commit 224e8c835f
2 changed files with 77 additions and 139 deletions

View File

@ -46,6 +46,22 @@ namespace traits {
template <std::size_t I, typename... Args> template <std::size_t I, typename... Args>
using at_t = decltype(std::get<I>(std::declval<std::tuple<Args...>>())); using at_t = decltype(std::get<I>(std::declval<std::tuple<Args...>>()));
namespace detail {
template <typename T, typename... Args>
struct index_of_impl;
template <typename T, typename... Args>
struct index_of_impl<T, T, Args...> : std::integral_constant<std::size_t, 0U> {
};
template <typename T, typename U, typename... Args>
struct index_of_impl<T, U, Args...>
: std::integral_constant<std::size_t,
1 + index_of_impl<T, Args...>::value> {};
} // namespace detail
/// Evaluates to the index of T in the given pack
template <typename T, typename... Args>
using index_of_t = detail::index_of_impl<T, Args...>;
/// A tagging type for wrapping other types /// A tagging type for wrapping other types
template <typename... T> template <typename... T>
struct identity {}; struct identity {};

View File

@ -9,11 +9,11 @@
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is_slot hereby granted, free of charge, to any person obtaining a
of this software and associated documentation files(the "Software"), to deal copy of this software and associated documentation files(the "Software"), to
in the Software without restriction, including without limitation the rights deal in the Software without restriction, including without limitation the
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell rights to use, copy, modify, merge, publish, distribute, sublicense, and / or
copies of the Software, and to permit persons to whom the Software is sell copies of the Software, and to permit persons to whom the Software is_slot
furnished to do so, subject to the following conditions : furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
@ -33,6 +33,7 @@
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <limits>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -54,16 +55,24 @@ constexpr std::size_t max_size(std::initializer_list<std::size_t> list) {
return m; return m;
} }
/// Declares the aligned storage union for the given types
template <typename... T> template <typename... T>
using storage_of_t = using storage_of_t =
std::aligned_storage_t<max_size({sizeof(T)...}), max_size({alignof(T)...})>; std::aligned_storage_t<max_size({sizeof(T)...}), max_size({alignof(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> template <typename T>
struct optional_variant_base { struct optional_variant_base {
storage_of_t<T> storage_; storage_of_t<T> storage_;
std::uint8_t slot_; slot_t slot_;
constexpr optional_variant_base() : slot_(0U) { constexpr optional_variant_base() : slot_(empty_slot::value) {
} }
optional_variant_base(optional_variant_base const&) noexcept { optional_variant_base(optional_variant_base const&) noexcept {
@ -89,14 +98,14 @@ struct optional_variant_move_base {
assert(!other.is_empty()); assert(!other.is_empty());
#ifndef _NDEBUG #ifndef _NDEBUG
me.set(slot_t::empty); me.set_slot(empty_slot::value);
#endif #endif
other.visit([&](auto&& value) { other.visit([&](auto&& value) {
// ... // ...
me.init(std::move(value)); me.init(std::move(value));
}); });
me.set(other.get()); me.set_slot(other.get());
other.destroy(); other.destroy();
} }
optional_variant_move_base& optional_variant_move_base&
@ -112,7 +121,7 @@ struct optional_variant_move_base {
// ... // ...
me.init(std::move(value)); me.init(std::move(value));
}); });
me.set(other.get()); me.set_slot(other.get());
other.destroy(); other.destroy();
return *this; return *this;
} }
@ -131,14 +140,14 @@ struct optional_variant_copy_base : optional_variant_copy_base<Base> {
assert(!other.is_empty()); assert(!other.is_empty());
#ifndef _NDEBUG #ifndef _NDEBUG
me.set(slot_t::empty); me.set_slot(empty_slot::value);
#endif #endif
other.visit([&](auto&& value) { other.visit([&](auto&& value) {
// ... // ...
me.init(std::move(value)); me.init(std::move(value));
}); });
me.set(other.get()); me.set_slot(other.get());
} }
optional_variant_copy_base& operator=(optional_variant_copy_base&&) = default; optional_variant_copy_base& operator=(optional_variant_copy_base&&) = default;
optional_variant_copy_base& operator=(optional_variant_copy_base const& right) optional_variant_copy_base& operator=(optional_variant_copy_base const& right)
@ -154,7 +163,7 @@ struct optional_variant_copy_base : optional_variant_copy_base<Base> {
// ... // ...
me.init(std::move(value)); me.init(std::move(value));
}); });
me.set(other.get()); me.set_slot(other.get());
return *this; return *this;
} }
}; };
@ -168,21 +177,22 @@ struct optional_variant_copy_base<Base, false>
default; default;
optional_variant_copy_base& optional_variant_copy_base&
operator=(optional_variant_copy_base const&) = delete; operator=(optional_variant_copy_base const&) = delete;
optional_variant_copy_base& optional_variant_copy_base& operator=(optional_variant_copy_base&&) = default;
operator=(optional_variant_copy_base&& right) = 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 } // namespace detail
/// A class similar to the one in the expected proposal, /// A class similar to the one in the optional_variant proposal,
/// however it is capable of carrying an exception_ptr if /// however it is_slot capable of carrying an exception_ptr if
/// exceptions are used. /// exceptions are used.
template <typename... T> template <typename... T>
class optional_variant class optional_variant : detail::optional_variant_copy_base<
: detail::optional_variant_copy_base< optional_variant<T...>,
optional_variant<T...>, detail::every<std::is_copy_constructible, T...>>,
std::is_copy_constructible<types::error_type>::value && detail::optional_variant_base<T...> {
std::is_copy_constructible<T>::value>,
detail::optional_variant_base<T...> {
template <typename> template <typename>
friend class optional_variant; friend class optional_variant;
@ -195,84 +205,48 @@ class optional_variant
optional_variant(V&& value, detail::slot_t const slot) { optional_variant(V&& value, detail::slot_t const slot) {
using type = std::decay_t<decltype(value)>; using type = std::decay_t<decltype(value)>;
new (&this->storage_) type(std::forward<V>(value)); new (&this->storage_) type(std::forward<V>(value));
set(slot); set_slot(slot);
} }
public: public:
constexpr expected() = default; constexpr optional_variant() = default;
expected(expected const&) = default; optional_variant(optional_variant const&) = default;
expected(expected&&) = default; optional_variant(optional_variant&&) = default;
expected& operator=(expected const&) = default; optional_variant& operator=(optional_variant const&) = default;
expected& operator=(expected&&) = default; optional_variant& operator=(optional_variant&&) = default;
~expected() noexcept( ~optional_variant() noexcept(
std::is_nothrow_destructible<T>::value&& detail::every<std::is_nothrow_destructible, T...>::value) {
std::is_nothrow_destructible<types::error_type>::value) {
weak_destroy(); weak_destroy();
} }
explicit expected(T value) // template <typename V, std::size_t Index =
: expected(std::move(value), detail::slot_t::value) { traits::index_of_t<std::decay_t<V>, T...>::value>
} explicit optional_variant(V&& value) //
explicit expected(types::error_type error) // : optional_variant(std::forward<V>(value), Index) {
: expected(std::move(error), detail::slot_t::error) {
} }
expected& operator=(T value) { optional_variant& operator=(T value) {
set_value(std::move(value)); set_value(std::move(value));
return *this; return *this;
} }
expected& operator=(types::error_type error) { optional_variant& operator=(types::error_type error) {
set_exception(std::move(error)); set_exception(std::move(error));
return *this; return *this;
} }
bool is_value() const noexcept { template <typename V, std::size_t Index =
assert(!is_empty()); traits::index_of_t<std::decay_t<V>, T...>::value>
return is(detail::slot_t::value); bool is() const noexcept {
return is_slot(Index);
} }
bool is_exception() const noexcept {
assert(!is_empty()); bool is_empty() const noexcept {
return is(detail::slot_t::error); return is_slot(detail::empty_slot::value);
} }
explicit constexpr operator bool() const noexcept { explicit constexpr operator bool() const noexcept {
return is_value(); return !is_empty();
}
void set_value(T value) {
weak_destroy();
init(std::move(value));
set(detail::slot_t::value);
}
void set_exception(types::error_type error) {
weak_destroy();
init(std::move(error));
set(detail::slot_t::error);
}
T& get_value() noexcept {
assert(is_value());
return cast<T>();
}
T const& get_value() const noexcept {
assert(is_value());
return cast<T>();
}
types::error_type& get_exception() noexcept {
assert(is_exception());
return cast<types::error_type>();
}
types::error_type const& get_exception() const noexcept {
assert(is_exception());
return cast<types::error_type>();
}
T& operator*() noexcept {
return get_value();
}
T const& operator*() const noexcept {
return get_value();
} }
private: private:
@ -284,7 +258,7 @@ private:
case detail::slot_t::error: case detail::slot_t::error:
return std::forward<V>(visitor)(cast<types::error_type>()); return std::forward<V>(visitor)(cast<types::error_type>());
default: default:
// We don't visit when there is no value // We don't visit when there is_slot no value
break; break;
} }
} }
@ -296,15 +270,11 @@ private:
case detail::slot_t::error: case detail::slot_t::error:
return std::forward<V>(visitor)(cast<types::error_type>()); return std::forward<V>(visitor)(cast<types::error_type>());
default: default:
// We don't visit when there is no value // We don't visit when there is_slot no value
break; break;
} }
} }
bool is_empty() const noexcept {
return is(detail::slot_t::empty);
}
template <typename V> template <typename V>
V& cast() noexcept { V& cast() noexcept {
assert(!is_empty()); assert(!is_empty());
@ -326,7 +296,7 @@ private:
weak_destroy(); weak_destroy();
#ifdef NDEBUG #ifdef NDEBUG
set(detail::slot_t::empty); set_slot(detail::empty_slot::value);
#endif #endif
} }
void weak_destroy() { void weak_destroy() {
@ -336,67 +306,19 @@ private:
}); });
#ifndef NDEBUG #ifndef NDEBUG
set(detail::slot_t::empty); set_slot(detail::empty_slot::value);
#endif #endif
} }
detail::slot_t get() const noexcept { detail::slot_t get() const noexcept {
return this->slot_; return this->slot_;
} }
bool is(detail::slot_t const slot) const noexcept { bool is_slot(detail::slot_t const slot) const noexcept {
return get() == slot; return get() == slot;
} }
void set(detail::slot_t const slot) { void set_slot(detail::slot_t const slot) {
this->slot_ = slot; this->slot_ = slot;
} }
}; };
namespace detail {
struct void_guard_tag {};
template <typename T>
struct expected_result_trait;
template <>
struct expected_result_trait<traits::identity<>> {
using expected_type = expected<void_guard_tag>;
static constexpr void_guard_tag wrap() noexcept {
return {};
}
static void unwrap(expected_type&& e) {
assert(e.is_value());
(void)e;
}
};
template <typename T>
struct expected_result_trait<traits::identity<T>> {
using expected_type = expected<T>;
static auto wrap(T arg) {
return std::move(arg);
}
static auto unwrap(expected_type&& e) {
assert(e.is_value());
return std::move(e.get_value());
}
};
template <typename First, typename Second, typename... Rest>
struct expected_result_trait<traits::identity<First, Second, Rest...>> {
using expected_type = expected<std::tuple<First, Second, Rest...>>;
static auto wrap(First first, Second second, Rest... rest) {
return std::make_tuple(std::move(first), std::move(second),
std::move(rest)...);
}
static auto unwrap(expected_type&& e) {
assert(e.is_value());
return std::move(e.get_value());
}
};
} // namespace detail
template <typename Continuable>
using expected_result_trait_t = detail::expected_result_trait<decltype(
hints::hint_of(traits::identify<Continuable>{}))>;
} // namespace util } // namespace util
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti