blob: 5e168b23516d63af447dba63e2e77c921cb92ecf [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_FIT_VARIANT_H_
#define LIB_FIT_VARIANT_H_
#include <exception>
#include <new>
#include <type_traits>
#include <utility>
#include "constructors_internal.h"
#include "in_place_internal.h"
#include "storage_internal.h"
#include "traits.h"
#include "utility_internal.h"
namespace fit {
// A default-constructible type that may be used as the first variant type to
// make fit::variant default-constructible when other variants are not. This
// type may also be used as an alternative representing an empty value.
struct monostate final {
constexpr bool operator==(const monostate& other) const { return true; }
constexpr bool operator!=(const monostate& other) const { return false; }
constexpr bool operator<(const monostate& other) const { return false; }
constexpr bool operator>(const monostate& other) const { return false; }
constexpr bool operator<=(const monostate& other) const { return true; }
constexpr bool operator>=(const monostate& other) const { return true; }
};
namespace internal {
// Helper type to avoid recursive instantiations of the full variant type.
template <typename...>
struct variant_list {};
// Gets the number of alternatives in a variant_list as a compile-time constant.
template <typename T>
struct variant_list_size;
template <typename... Ts>
struct variant_list_size<variant_list<Ts...>> : std::integral_constant<size_t, sizeof...(Ts)> {};
// Helper to get the type of a variant_list alternative with the given index.
template <size_t Index, typename VariantList>
struct variant_alternative;
template <size_t Index, typename T0, typename... Ts>
struct variant_alternative<Index, variant_list<T0, Ts...>>
: variant_alternative<Index - 1, variant_list<Ts...>> {};
template <typename T0, typename... Ts>
struct variant_alternative<0, variant_list<T0, Ts...>> {
using type = T0;
};
} // namespace internal
// Forward declaration.
template <typename... Ts>
class variant;
// Gets the type of a variant alternative with the given index.
template <size_t Index, typename Variant>
struct variant_alternative;
template <size_t Index, typename... Ts>
struct variant_alternative<Index, variant<Ts...>>
: ::fit::internal::variant_alternative<Index, ::fit::internal::variant_list<Ts...>> {};
template <size_t index, typename Variant>
using variant_alternative_t = typename variant_alternative<index, Variant>::type;
// Gets the number of alternatives in a variant as a compile-time constant
// expression.
template <typename T>
struct variant_size;
template <typename... Ts>
struct variant_size<variant<Ts...>> : std::integral_constant<size_t, sizeof...(Ts)> {};
template <typename T>
struct variant_size<const T> : variant_size<T> {};
template <typename T>
struct variant_size<volatile T> : variant_size<T> {};
template <typename T>
struct variant_size<const volatile T> : variant_size<T> {};
#ifdef __cpp_inline_variables
template <typename T>
inline constexpr size_t variant_size_v = variant_size<T>::value;
#else
template <typename T>
struct variant_size_holder {
static constexpr size_t value{variant_size<T>::value};
};
template <typename T>
constexpr size_t variant_size_holder<T>::value;
template <typename T>
static constexpr const size_t& variant_size_v = variant_size_holder<T>::value;
#endif
// Exception type to report bad accesses to variant.
class bad_variant_access : public std::exception {
public:
bad_variant_access() noexcept {}
const char* what() const noexcept override { return reason_; }
private:
template <typename... Ts>
friend class variant;
bad_variant_access(const char* reason) noexcept : reason_{reason} {}
// String describing the reason for the bad access. Must point to a string
// with static storage duration.
const char* reason_;
};
namespace internal {
// Helper type to check that conversions do not narrow.
template <typename T>
struct check_narrow {
T x[1];
};
// Builds a check(Ti) function for each alternative Ti. This trait is evaluated
// for each element of fit::variant<Ts...>. Essentially: for (Index, Ti) in Ts.
//
// Index is the zero-based index of the corresponding element Ti in the pack Ts.
// T is the type deduced from the converting constructor or assignment operator
// of fit::variant for which we want to find an appropriately convertible
// element.
//
// The specializations below match the element Ti that passes the conversion
// checks.
template <size_t Index, typename T, typename Ti,
bool IsBool = std::is_same<bool, std::remove_cv_t<Ti>>::value, typename = void>
struct check_valid_option {
// Non-static so that check_valid_option<...>::check is always a valid
// name, but doesn't participate in the overload resolution in the
// valid_option_index selection trait.
void check();
};
// Checks that Ti x[] = {std::forward<T>()} is well formed.
template <size_t Index, typename T, typename Ti>
struct check_valid_option<Index, T, Ti, false,
void_t<decltype(check_narrow<Ti>{{std::declval<T>()}})>> {
static std::integral_constant<size_t, Index> check(Ti);
};
// Checks that remove_cvref_t<T> is bool when Ti is cv bool.
template <size_t Index, typename T, typename Ti>
struct check_valid_option<Index, T, Ti, true,
std::enable_if_t<std::is_same<bool, remove_cvref_t<T>>::value>> {
static std::integral_constant<size_t, Index> check(Ti);
};
// Mixes in instantiations of check_valid_option for each element in
// fit::variant<Ts...>, creating a set of check(Ti) functions that might match
// T following the conversion rules.
template <typename T, typename VariantList,
typename = std::make_index_sequence<variant_list_size<VariantList>::value>>
struct find_valid_option {
// Non-static so that find_valid_option<...>::check is always a valid name
// in the using clause of the recursive case, but doesn't participate in the
// overload resolution in the valid_option_index trait.
void check();
};
// Recursive case. This would be simpler with C++17 pack expansion in using
// directives, but this must function in C++14.
template <typename T, typename Ti, size_t Index, typename... Ts, size_t... Is>
struct find_valid_option<T, variant_list<Ti, Ts...>, std::index_sequence<Index, Is...>>
: check_valid_option<Index, T, Ti>,
find_valid_option<T, variant_list<Ts...>, std::index_sequence<Is...>> {
// Introduce the base class definitions of check() into this scope. The
// static check(Ti) methods participate in overload resolution in the
// valid_option_index trait, while the non-static check() methods are
// ignored.
using check_valid_option<Index, T, Ti>::check;
using find_valid_option<T, variant_list<Ts...>, std::index_sequence<Is...>>::check;
};
// Evaluates to the index of the valid target type Ti selected from
// fit::variant<Ts...>. The type expression is well formed IFF a single valid
// target type is available that converts from T.
template <typename T, typename VariantList>
using valid_option_index = decltype(find_valid_option<T, VariantList>::check(std::declval<T>()));
// Evaluates to the index of the valid target Ti that converts from T or the
// reserved empty index when no uniquely suitable option is available.
template <typename T, typename Variant, typename = void>
struct selected_index : std::integral_constant<size_t, ::fit::internal::empty_index> {};
template <typename T, typename... Ts>
struct selected_index<T, variant<Ts...>, void_t<valid_option_index<T, variant_list<Ts...>>>>
: valid_option_index<T, variant_list<Ts...>> {};
} // namespace internal
// A resonably complete implementation of std::variant compatible with C++14.
template <typename... Ts>
class variant
: private ::fit::internal::modulate_default_constructor<::fit::internal::first_t<Ts...>>,
private ::fit::internal::modulate_copy_and_move<Ts...> {
private:
static_assert(sizeof...(Ts) > 0, "Variant must have at least one type!");
static constexpr bool nothrow_default_constructible =
std::is_nothrow_default_constructible<::fit::internal::first_t<Ts...>>::value;
static constexpr bool nothrow_move_constructible =
conjunction_v<std::is_nothrow_move_constructible<Ts>...>;
static constexpr auto default_init_v = ::fit::internal::default_init_v;
static constexpr auto trivial_init_v = ::fit::internal::trivial_init_v;
template <typename T>
using type_tag = ::fit::internal::type_tag<T>;
template <size_t Index>
using index_tag = ::fit::internal::index_tag<Index>;
template <typename T>
using not_self_type = ::fit::internal::not_same_type<variant, T>;
template <typename T>
using not_in_place = ::fit::internal::not_same_type<in_place_t, T>;
template <typename T>
struct occurs_once
: std::integral_constant<bool, ::fit::internal::occurences_of_v<T, Ts...> == 1> {};
template <typename... Conditions>
using requires_conditions = ::fit::internal::requires_conditions<Conditions...>;
template <typename... Conditions>
using assignment_requires_conditions =
::fit::internal::assignment_requires_conditions<variant&, Conditions...>;
template <typename T, typename... Args>
using emplace_constructible_by_type =
std::enable_if_t<(::fit::internal::occurences_of_v<T, Ts...> == 1 &&
std::is_constructible<T, Args...>::value),
std::add_lvalue_reference_t<T>>;
template <size_t Index, typename = std::enable_if_t<(Index < sizeof...(Ts))>>
using alternative_t = variant_alternative_t<Index, variant>;
template <size_t Index, typename... Args>
using emplace_constructible_by_index =
std::enable_if_t<std::is_constructible<alternative_t<Index>, Args...>::value,
std::add_lvalue_reference_t<alternative_t<Index>>>;
template <typename T>
static constexpr size_t selected_index = ::fit::internal::selected_index<T, variant>::value;
template <typename T, typename = std::enable_if<not_self_type<T>::value>>
using selected_t = alternative_t<selected_index<T>>;
[[noreturn]] static constexpr void throw_bad_variant_access(const char* reason) {
#if __cpp_exceptions
throw bad_variant_access(reason);
#else
(void)reason;
__builtin_abort();
#endif
}
public:
// Default constructors.
constexpr variant() noexcept(nothrow_default_constructible) : storage_{default_init_v} {}
// Copy/move constructors and assignment operators.
constexpr variant(const variant&) = default;
constexpr variant& operator=(const variant&) = default;
constexpr variant(variant&&) noexcept(nothrow_move_constructible) = default;
constexpr variant& operator=(variant&&) = default;
// Converting constructors.
template <typename T,
requires_conditions<std::integral_constant<bool, (sizeof...(Ts) > 0)>,
not_in_place<T>> = true,
typename Ti = selected_t<T&&>,
requires_conditions<occurs_once<Ti>, std::is_constructible<Ti, T>> = true>
constexpr variant(T&& value) noexcept(std::is_nothrow_constructible<Ti, T>::value)
: storage_(type_tag<Ti>{}, std::forward<T>(value)) {}
template <typename T, typename... Args,
requires_conditions<occurs_once<T>, std::is_constructible<T, Args...>> = true>
explicit constexpr variant(in_place_type_t<T>, Args&&... args)
: storage_(type_tag<T>{}, std::forward<Args>(args)...) {}
template <typename T, typename U, typename... Args,
requires_conditions<occurs_once<T>, std::is_constructible<T, std::initializer_list<T>&,
Args...>> = true>
explicit constexpr variant(in_place_type_t<T>, std::initializer_list<U> init_list, Args&&... args)
: storage_(type_tag<T>{}, init_list, std::forward<Args>(args)...) {}
template <size_t Index, typename... Args,
requires_conditions<std::is_constructible<alternative_t<Index>, Args...>> = true>
explicit constexpr variant(in_place_index_t<Index>, Args&&... args)
: storage_(index_tag<Index>{}, std::forward<Args>(args)...) {}
template <size_t Index, typename U, typename... Args,
requires_conditions<std::is_constructible<alternative_t<Index>,
std::initializer_list<U>&, Args...>> = true>
explicit constexpr variant(in_place_index_t<Index>, std::initializer_list<U> init_list,
Args&&... args)
: storage_(index_tag<Index>{}, init_list, std::forward<Args>(args)...) {}
~variant() = default;
// Converting assignment.
template <typename T>
constexpr assignment_requires_conditions<
occurs_once<selected_t<T>>, std::is_constructible<selected_t<T&&>, T>,
std::is_assignable<selected_t<T&&>&, T>,
disjunction<std::is_nothrow_constructible<selected_t<T&&>, T>,
negation<std::is_nothrow_move_constructible<selected_t<T&&>>>>>
operator=(T&& value) noexcept(std::is_nothrow_assignable<selected_t<T&&>&, T>::value&&
std::is_nothrow_constructible<selected_t<T&&>, T>::value) {
constexpr auto index = selected_index<T>;
if (storage_.index() == index) {
storage_.get(index_tag<index>{}) = std::forward<T>(value);
} else {
this->emplace<index>(std::forward<T>(value));
}
return *this;
}
template <typename T>
constexpr assignment_requires_conditions<
occurs_once<selected_t<T>>, std::is_constructible<selected_t<T&&>, T>,
std::is_assignable<selected_t<T&&>&, T>,
conjunction<negation<std::is_nothrow_constructible<selected_t<T&&>, T>>,
std::is_nothrow_move_constructible<selected_t<T&&>>>>
operator=(T&& value) noexcept(std::is_nothrow_assignable<selected_t<T&&>&, T>::value&&
std::is_nothrow_constructible<selected_t<T&&>, T>::value) {
constexpr auto index = selected_index<T>;
if (storage_.index() == index) {
storage_.get(index_tag<index>{}) = std::forward<T>(value);
} else {
this->operator=(variant(std::forward<T>(value)));
}
return *this;
}
constexpr size_t index() const noexcept { return storage_.index(); }
// TODO(eieio): Remove uses of these in favor of non-member get.
template <size_t Index>
constexpr auto& get() & {
if (storage_.has_value()) {
return storage_.get(index_tag<Index>{});
} else {
throw_bad_variant_access("Access to empty variant!");
}
}
template <size_t Index>
constexpr const auto& get() const& {
if (storage_.has_value()) {
return storage_.get(index_tag<Index>{});
} else {
throw_bad_variant_access("Access to empty variant!");
}
}
template <size_t Index>
constexpr auto&& get() && {
if (storage_.has_value()) {
return std::move(storage_.get(index_tag<Index>{}));
} else {
throw_bad_variant_access("Access to empty variant!");
}
}
template <size_t Index>
constexpr const auto&& get() const&& {
if (storage_.has_value()) {
return std::move(storage_.get(index_tag<Index>{}));
} else {
throw_bad_variant_access("Access to empty variant!");
}
}
template <typename T>
constexpr auto& get() & {
if (storage_.has_value()) {
return storage_.get(type_tag<T>{});
} else {
throw_bad_variant_access("Access to empty variant!");
}
}
template <typename T>
constexpr const auto& get() const& {
if (storage_.has_value()) {
return storage_.get(type_tag<T>{});
} else {
throw_bad_variant_access("Access to empty variant!");
}
}
template <typename T>
constexpr auto&& get() && {
if (storage_.has_value()) {
return std::move(storage_.get(type_tag<T>{}));
} else {
throw_bad_variant_access("Access to empty variant!");
}
}
template <typename T>
constexpr const auto&& get() const&& {
if (storage_.has_value()) {
return std::move(storage_.get(type_tag<T>{}));
} else {
throw_bad_variant_access("Access to empty variant!");
}
}
// Emplacement.
template <typename T, typename... Args>
constexpr emplace_constructible_by_type<T, Args&&...> emplace(Args&&... args) {
storage_.reset();
storage_.construct(type_tag<T>{}, std::forward<Args>(args)...);
return storage_.get(type_tag<T>{});
}
template <typename T, typename U, typename... Args>
constexpr emplace_constructible_by_type<T, std::initializer_list<U>&, Args&&...> emplace(
std::initializer_list<U> init_list, Args&&... args) {
storage_.reset();
storage_.construct(type_tag<T>{}, init_list, std::forward<Args>(args)...);
return storage_.get(type_tag<T>{});
}
template <size_t Index, typename... Args>
constexpr emplace_constructible_by_index<Index, Args&&...> emplace(Args&&... args) {
storage_.reset();
storage_.construct(index_tag<Index>{}, std::forward<Args>(args)...);
return storage_.get(index_tag<Index>{});
}
template <size_t Index, typename U, typename... Args>
constexpr emplace_constructible_by_index<Index, std::initializer_list<U>&, Args&&...> emplace(
std::initializer_list<U> init_list, Args&&... args) {
storage_.reset();
storage_.construct(index_tag<Index>{}, init_list, std::forward<Args>(args)...);
return storage_.get(index_tag<Index>{});
}
// Swap.
void swap(variant& other) { storage_.swap(other.storage_); }
// Comparison.
friend constexpr bool operator==(const variant& lhs, const variant& rhs) {
bool result = false;
const bool has_value =
lhs.storage_.visit([&result, &lhs, &rhs](auto, auto active_index_tag, const auto*) {
if (lhs.index() != rhs.index()) {
result = false;
} else {
result = lhs.storage_.get(active_index_tag) == rhs.storage_.get(active_index_tag);
}
});
return !has_value || result;
}
friend constexpr bool operator!=(const variant& lhs, const variant& rhs) {
bool result = true;
const bool has_value =
lhs.storage_.visit([&result, &lhs, &rhs](auto, auto active_index_tag, const auto*) {
if (lhs.index() != rhs.index()) {
result = true;
} else {
result = lhs.storage_.get(active_index_tag) != rhs.storage_.get(active_index_tag);
}
});
return has_value && result;
}
friend constexpr bool operator<(const variant& lhs, const variant& rhs) {
bool result = true;
const bool has_value =
rhs.storage_.visit([&result, &lhs, &rhs](auto, auto active_index_tag, const auto*) {
if (!lhs.storage_.has_value()) {
result = true;
} else if (lhs.index() < rhs.index()) {
result = true;
} else if (lhs.index() > rhs.index()) {
result = false;
} else {
result = lhs.storage_.get(active_index_tag) < rhs.storage_.get(active_index_tag);
}
});
return has_value && result;
}
friend constexpr bool operator>(const variant& lhs, const variant& rhs) {
bool result = true;
const bool has_value =
lhs.storage_.visit([&result, &lhs, &rhs](auto, auto active_index_tag, const auto*) {
if (!rhs.storage_.has_value()) {
result = true;
} else if (lhs.index() > rhs.index()) {
result = true;
} else if (lhs.index() < rhs.index()) {
result = false;
} else {
result = lhs.storage_.get(active_index_tag) > rhs.storage_.get(active_index_tag);
}
});
return has_value && result;
}
friend constexpr bool operator<=(const variant& lhs, const variant& rhs) {
bool result = false;
const bool has_value =
lhs.storage_.visit([&result, &lhs, &rhs](auto, auto active_index_tag, const auto*) {
if (!rhs.storage_.has_value()) {
result = false;
} else if (lhs.index() < rhs.index()) {
result = true;
} else if (lhs.index() > rhs.index()) {
result = false;
} else {
result = lhs.storage_.get(active_index_tag) <= rhs.storage_.get(active_index_tag);
}
});
return !has_value || result;
}
friend constexpr bool operator>=(const variant& lhs, const variant& rhs) {
bool result = false;
const bool has_value =
rhs.storage_.visit([&result, &lhs, &rhs](auto, auto active_index_tag, const auto*) {
if (!lhs.storage_.has_value()) {
result = false;
} else if (lhs.index() > rhs.index()) {
result = true;
} else if (lhs.index() < rhs.index()) {
result = false;
} else {
result = lhs.storage_.get(active_index_tag) >= rhs.storage_.get(active_index_tag);
}
});
return !has_value || result;
}
private:
::fit::internal::storage_type<Ts...> storage_;
};
// Swaps variants.
template <typename... Ts>
void swap(variant<Ts...>& a, variant<Ts...>& b) {
a.swap(b);
}
// Accesses the variant by zero-based index.
//
// Accesses should use ADL, similar to the pattern for std::swap:
//
// using std::get;
// get<some_index>(some_fit_variant);
//
// This makes code adaptable to substituting std::variant for fit::variant on
// newer compilers.
template <size_t Index, typename... Ts>
constexpr auto& get(variant<Ts...>& value) {
return value.template get<Index>();
}
template <size_t Index, typename... Ts>
constexpr auto&& get(variant<Ts...>&& value) {
return std::move(value).template get<Index>();
}
template <size_t Index, typename... Ts>
constexpr const auto& get(const variant<Ts...>& value) {
return value.template get<Index>();
}
template <size_t Index, typename... Ts>
constexpr const auto&& get(const variant<Ts...>&& value) {
return std::move(value).template get<Index>();
}
// Accesses the variant by unique type. See note above about ADL.
template <typename T, typename... Ts>
constexpr auto& get(variant<Ts...>& value) {
return value.template get<T>();
}
template <typename T, typename... Ts>
constexpr auto&& get(variant<Ts...>&& value) {
return std::move(value).template get<T>();
}
template <typename T, typename... Ts>
constexpr const auto& get(const variant<Ts...>& value) {
return value.template get<T>();
}
template <typename T, typename... Ts>
constexpr const auto&& get(const variant<Ts...>&& value) {
return std::move(value).template get<T>();
}
// Checks if the variant holds type T. See note above about ADL.
template <typename T, typename... Ts>
constexpr bool holds_alternative(const variant<Ts...>& value) {
constexpr auto index = ::fit::internal::selected_index<T, variant<Ts...>>::value;
return value.index() == index;
}
// TODO(eieio): Remove once the old ::fit::internal spellings of these types is
// removed from FIDL.
namespace internal {
using ::fit::monostate;
using ::fit::variant;
} // namespace internal
} // namespace fit
#endif // LIB_FIT_VARIANT_H_