850 lines
26 KiB
C++
850 lines
26 KiB
C++
|
|
/*
|
||
|
|
MIT License
|
||
|
|
|
||
|
|
Copyright (c) 2021 Jean-Baptiste Vallon Hoarau
|
||
|
|
|
||
|
|
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 SWL_CPP_LIBRARY_VARIANT_HPP
|
||
|
|
#define SWL_CPP_LIBRARY_VARIANT_HPP
|
||
|
|
|
||
|
|
#include <exception>
|
||
|
|
#include <type_traits>
|
||
|
|
#include <utility> // swap
|
||
|
|
#include <limits> // used for index_type
|
||
|
|
#include <initializer_list>
|
||
|
|
|
||
|
|
// a user-provided header for tweaks
|
||
|
|
// possible macros defined :
|
||
|
|
// SWL_VARIANT_NO_CONSTEXPR_EMPLACE
|
||
|
|
// SWL_VARIANT_NO_STD_HASH
|
||
|
|
#if __has_include("swl_variant_knobs.hpp")
|
||
|
|
#include "swl_variant_knobs.hpp"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef SWL_VARIANT_NO_CONSTEXPR_EMPLACE
|
||
|
|
#include <memory>
|
||
|
|
#else
|
||
|
|
#include <new>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef SWL_VARIANT_NO_STD_HASH
|
||
|
|
#include <functional>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef __has_builtin
|
||
|
|
#define __has_builtin(x) 0
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define SWL_FWD(x) static_cast<decltype(x)&&>(x)
|
||
|
|
#define SWL_MOV(x) static_cast< std::remove_reference_t<decltype(x)>&& >(x)
|
||
|
|
|
||
|
|
#ifdef SWL_VARIANT_DEBUG
|
||
|
|
#include <iostream>
|
||
|
|
#define DebugAssert(X) if (not (X)) std::cout << "Variant : assertion failed : [" << #X << "] at line : " << __LINE__ << "\n";
|
||
|
|
#undef SWL_VARIANT_DEBUG
|
||
|
|
#else
|
||
|
|
#define DebugAssert(X)
|
||
|
|
#endif
|
||
|
|
|
||
|
|
namespace swl {
|
||
|
|
|
||
|
|
class bad_variant_access final : public std::exception {
|
||
|
|
const char* message = ""; // llvm test requires a well formed what() on default init
|
||
|
|
public :
|
||
|
|
bad_variant_access(const char* str) noexcept : message{str} {}
|
||
|
|
bad_variant_access() noexcept = default;
|
||
|
|
bad_variant_access(const bad_variant_access&) noexcept = default;
|
||
|
|
bad_variant_access& operator= (const bad_variant_access&) noexcept = default;
|
||
|
|
const char* what() const noexcept override { return message; }
|
||
|
|
};
|
||
|
|
|
||
|
|
namespace vimpl {
|
||
|
|
struct variant_tag{};
|
||
|
|
struct emplacer_tag{};
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
struct in_place_type_t : private vimpl::emplacer_tag {};
|
||
|
|
|
||
|
|
template <std::size_t Index>
|
||
|
|
struct in_place_index_t : private vimpl::emplacer_tag {};
|
||
|
|
|
||
|
|
template <std::size_t Index>
|
||
|
|
inline static constexpr in_place_index_t<Index> in_place_index;
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
inline static constexpr in_place_type_t<T> in_place_type;
|
||
|
|
|
||
|
|
namespace vimpl {
|
||
|
|
#include "variant_detail.hpp"
|
||
|
|
#include "variant_visit.hpp"
|
||
|
|
|
||
|
|
struct variant_npos_t {
|
||
|
|
template <class T>
|
||
|
|
constexpr bool operator==(T idx) const noexcept { return idx == std::numeric_limits<T>::max(); }
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
inline constexpr bool is_variant = std::is_base_of_v<vimpl::variant_tag, std::decay_t<T>>;
|
||
|
|
|
||
|
|
inline static constexpr vimpl::variant_npos_t variant_npos;
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
class variant;
|
||
|
|
|
||
|
|
// ill-formed variant, an empty specialization prevents some really bad errors messages on gcc
|
||
|
|
template <class... Ts>
|
||
|
|
requires (
|
||
|
|
(std::is_array_v<Ts> || ...)
|
||
|
|
|| (std::is_reference_v<Ts> || ...)
|
||
|
|
|| (std::is_void_v<Ts> || ...)
|
||
|
|
|| sizeof...(Ts) == 0
|
||
|
|
)
|
||
|
|
class variant<Ts...> {
|
||
|
|
static_assert( sizeof...(Ts) > 0, "A variant cannot be empty.");
|
||
|
|
static_assert( not (std::is_reference_v<Ts> || ...), "A variant cannot contain references, consider using reference wrappers instead." );
|
||
|
|
static_assert( not (std::is_void_v<Ts> || ...), "A variant cannot contains void." );
|
||
|
|
static_assert( not (std::is_array_v<Ts> || ...), "A variant cannot contain a raw array type, consider using std::array instead." );
|
||
|
|
};
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
class variant : private vimpl::variant_tag {
|
||
|
|
|
||
|
|
using storage_t = vimpl::union_node<false, vimpl::make_tree_union<Ts...>, vimpl::dummy_type>;
|
||
|
|
|
||
|
|
static constexpr bool is_trivial = std::is_trivial_v<storage_t>;
|
||
|
|
static constexpr bool has_copy_ctor = std::is_copy_constructible_v<storage_t>;
|
||
|
|
static constexpr bool trivial_copy_ctor = is_trivial || std::is_trivially_copy_constructible_v<storage_t>;
|
||
|
|
static constexpr bool has_copy_assign = std::is_copy_constructible_v<storage_t>;
|
||
|
|
static constexpr bool trivial_copy_assign = is_trivial || std::is_trivially_copy_assignable_v<storage_t>;
|
||
|
|
static constexpr bool has_move_ctor = std::is_move_constructible_v<storage_t>;
|
||
|
|
static constexpr bool trivial_move_ctor = is_trivial || std::is_trivially_move_constructible_v<storage_t>;
|
||
|
|
static constexpr bool has_move_assign = std::is_move_assignable_v<storage_t>;
|
||
|
|
static constexpr bool trivial_move_assign = is_trivial || std::is_trivially_move_assignable_v<storage_t>;
|
||
|
|
static constexpr bool trivial_dtor = std::is_trivially_destructible_v<storage_t>;
|
||
|
|
|
||
|
|
public :
|
||
|
|
|
||
|
|
template <std::size_t Idx>
|
||
|
|
using alternative = std::remove_reference_t< decltype( std::declval<storage_t&>().template get<Idx>() ) >;
|
||
|
|
|
||
|
|
static constexpr bool can_be_valueless = not is_trivial;
|
||
|
|
|
||
|
|
static constexpr unsigned size = sizeof...(Ts);
|
||
|
|
|
||
|
|
using index_type = vimpl::smallest_suitable_integer_type<sizeof...(Ts) + can_be_valueless, unsigned char, unsigned short, unsigned>;
|
||
|
|
|
||
|
|
static constexpr index_type npos = -1;
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
static constexpr int index_of = vimpl::find_first_true( {std::is_same_v<T, Ts>...} );
|
||
|
|
|
||
|
|
// ============================================= constructors (20.7.3.2)
|
||
|
|
|
||
|
|
// default constructor
|
||
|
|
constexpr variant()
|
||
|
|
noexcept (std::is_nothrow_default_constructible_v<alternative<0>>)
|
||
|
|
requires std::is_default_constructible_v<alternative<0>>
|
||
|
|
: storage{ in_place_index<0> }, current{0}
|
||
|
|
{}
|
||
|
|
|
||
|
|
// copy constructor (trivial)
|
||
|
|
constexpr variant(const variant&)
|
||
|
|
requires trivial_copy_ctor
|
||
|
|
= default;
|
||
|
|
|
||
|
|
// note : both the copy and move constructor cannot be meaningfully constexpr without std::construct_at
|
||
|
|
// copy constructor
|
||
|
|
constexpr variant(const variant& o)
|
||
|
|
requires (has_copy_ctor and not trivial_copy_ctor)
|
||
|
|
: storage{ vimpl::dummy_type{} } {
|
||
|
|
construct_from(o);
|
||
|
|
}
|
||
|
|
|
||
|
|
// move constructor (trivial)
|
||
|
|
constexpr variant(variant&&)
|
||
|
|
requires trivial_move_ctor
|
||
|
|
= default;
|
||
|
|
|
||
|
|
// move constructor
|
||
|
|
constexpr variant(variant&& o)
|
||
|
|
noexcept ((std::is_nothrow_move_constructible_v<Ts> && ...))
|
||
|
|
requires (has_move_ctor and not trivial_move_ctor)
|
||
|
|
: storage{ vimpl::dummy_type{} } {
|
||
|
|
construct_from(static_cast<variant&&>(o));
|
||
|
|
}
|
||
|
|
|
||
|
|
// generic constructor
|
||
|
|
template <class T, class M = vimpl::best_overload_match<T&&, Ts...>, class D = std::decay_t<T>>
|
||
|
|
constexpr variant(T&& t)
|
||
|
|
noexcept ( std::is_nothrow_constructible_v<M, T&&> )
|
||
|
|
requires ( not std::is_same_v<D, variant> and not std::is_base_of_v<vimpl::emplacer_tag, D> )
|
||
|
|
: variant{ in_place_index< index_of<M> >, static_cast<T&&>(t) }
|
||
|
|
{}
|
||
|
|
|
||
|
|
// construct at index
|
||
|
|
template <std::size_t Index, class... Args>
|
||
|
|
requires (Index < size && std::is_constructible_v<alternative<Index>, Args&&...>)
|
||
|
|
explicit constexpr variant(in_place_index_t<Index> tag, Args&&... args)
|
||
|
|
: storage{tag, static_cast<Args&&>(args)...}, current(Index)
|
||
|
|
{}
|
||
|
|
|
||
|
|
// construct a given type
|
||
|
|
template <class T, class... Args>
|
||
|
|
requires (vimpl::appears_exactly_once<T, Ts...> && std::is_constructible_v<T, Args&&...>)
|
||
|
|
explicit constexpr variant(in_place_type_t<T>, Args&&... args)
|
||
|
|
: variant{ in_place_index< index_of<T> >, static_cast<Args&&>(args)... }
|
||
|
|
{}
|
||
|
|
|
||
|
|
// initializer-list constructors
|
||
|
|
template <std::size_t Index, class U, class... Args>
|
||
|
|
requires (
|
||
|
|
(Index < size) and
|
||
|
|
std::is_constructible_v< alternative<Index>, std::initializer_list<U>&, Args&&... >
|
||
|
|
)
|
||
|
|
explicit constexpr variant (in_place_index_t<Index> tag, std::initializer_list<U> list, Args&&... args)
|
||
|
|
: storage{ tag, list, SWL_FWD(args)... }, current{Index}
|
||
|
|
{}
|
||
|
|
|
||
|
|
template <class T, class U, class... Args>
|
||
|
|
requires (
|
||
|
|
vimpl::appears_exactly_once<T, Ts...>
|
||
|
|
&& std::is_constructible_v< T, std::initializer_list<U>&, Args&&... >
|
||
|
|
)
|
||
|
|
explicit constexpr variant (in_place_type_t<T>, std::initializer_list<U> list, Args&&... args)
|
||
|
|
: storage{ in_place_index<index_of<T>>, list, SWL_FWD(args)... }, current{index_of<T>}
|
||
|
|
{}
|
||
|
|
|
||
|
|
// ================================ destructors (20.7.3.3)
|
||
|
|
|
||
|
|
constexpr ~variant() requires trivial_dtor = default;
|
||
|
|
|
||
|
|
constexpr ~variant() requires (not trivial_dtor) {
|
||
|
|
reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ================================ assignment (20.7.3.4)
|
||
|
|
|
||
|
|
// copy assignment (trivial)
|
||
|
|
constexpr variant& operator=(const variant& o)
|
||
|
|
requires trivial_copy_assign && trivial_copy_ctor
|
||
|
|
= default;
|
||
|
|
|
||
|
|
// copy assignment
|
||
|
|
constexpr variant& operator=(const variant& rhs)
|
||
|
|
requires (has_copy_assign and not(trivial_copy_assign && trivial_copy_ctor))
|
||
|
|
{
|
||
|
|
assign_from(rhs, [this] (const auto& elem, auto index_cst) {
|
||
|
|
if (index() == index_cst)
|
||
|
|
unsafe_get<index_cst>() = elem;
|
||
|
|
else{
|
||
|
|
using type = alternative<index_cst>;
|
||
|
|
if constexpr (std::is_nothrow_copy_constructible_v<type>
|
||
|
|
or not std::is_nothrow_move_constructible_v<type>)
|
||
|
|
this->emplace<index_cst>(elem);
|
||
|
|
else{
|
||
|
|
alternative<index_cst> tmp = elem;
|
||
|
|
this->emplace<index_cst>( SWL_MOV(tmp) );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
// move assignment (trivial)
|
||
|
|
constexpr variant& operator=(variant&& o)
|
||
|
|
requires (trivial_move_assign and trivial_move_ctor and trivial_dtor)
|
||
|
|
= default;
|
||
|
|
|
||
|
|
// move assignment
|
||
|
|
constexpr variant& operator=(variant&& o)
|
||
|
|
noexcept ((std::is_nothrow_move_constructible_v<Ts> && ...) && (std::is_nothrow_move_assignable_v<Ts> && ...))
|
||
|
|
requires (has_move_assign && has_move_ctor and not(trivial_move_assign and trivial_move_ctor and trivial_dtor))
|
||
|
|
{
|
||
|
|
this->assign_from( SWL_FWD(o), [this] (auto&& elem, auto index_cst)
|
||
|
|
{
|
||
|
|
if (index() == index_cst)
|
||
|
|
this->unsafe_get<index_cst>() = SWL_MOV(elem);
|
||
|
|
else
|
||
|
|
this->emplace<index_cst>( SWL_MOV(elem) );
|
||
|
|
});
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
// generic assignment
|
||
|
|
template <class T>
|
||
|
|
requires vimpl::has_non_ambiguous_match<T, Ts...>
|
||
|
|
constexpr variant& operator=(T&& t)
|
||
|
|
noexcept( std::is_nothrow_assignable_v<vimpl::best_overload_match<T&&, Ts...>, T&&>
|
||
|
|
&& std::is_nothrow_constructible_v<vimpl::best_overload_match<T&&, Ts...>, T&&> )
|
||
|
|
{
|
||
|
|
using related_type = vimpl::best_overload_match<T&&, Ts...>;
|
||
|
|
constexpr auto new_index = index_of<related_type>;
|
||
|
|
|
||
|
|
if (this->current == new_index)
|
||
|
|
this->unsafe_get<new_index>() = SWL_FWD(t);
|
||
|
|
else {
|
||
|
|
|
||
|
|
constexpr bool do_simple_emplace =
|
||
|
|
std::is_nothrow_constructible_v<related_type, T>
|
||
|
|
or not std::is_nothrow_move_constructible_v<related_type>;
|
||
|
|
|
||
|
|
if constexpr ( do_simple_emplace )
|
||
|
|
this->emplace<new_index>( SWL_FWD(t) );
|
||
|
|
else {
|
||
|
|
related_type tmp = t;
|
||
|
|
this->emplace<new_index>( SWL_MOV(tmp) );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ================================== modifiers (20.7.3.5)
|
||
|
|
|
||
|
|
template <class T, class... Args>
|
||
|
|
requires (std::is_constructible_v<T, Args&&...> && vimpl::appears_exactly_once<T, Ts...>)
|
||
|
|
constexpr T& emplace(Args&&... args){
|
||
|
|
return emplace<index_of<T>>(static_cast<Args&&>(args)...);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <std::size_t Idx, class... Args>
|
||
|
|
requires (Idx < size and std::is_constructible_v<alternative<Idx>, Args&&...> )
|
||
|
|
constexpr auto& emplace(Args&&... args){
|
||
|
|
return emplace_impl<Idx>(SWL_FWD(args)...);
|
||
|
|
}
|
||
|
|
|
||
|
|
// emplace with initializer-lists
|
||
|
|
template <std::size_t Idx, class U, class... Args>
|
||
|
|
requires ( Idx < size
|
||
|
|
&& std::is_constructible_v<alternative<Idx>, std::initializer_list<U>&, Args&&...> )
|
||
|
|
constexpr auto& emplace(std::initializer_list<U> list, Args&&... args){
|
||
|
|
return emplace_impl<Idx>(list, SWL_FWD(args)...);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class U, class... Args>
|
||
|
|
requires ( std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>
|
||
|
|
&& vimpl::appears_exactly_once<T, Ts...> )
|
||
|
|
constexpr T& emplace(std::initializer_list<U> list, Args&&... args){
|
||
|
|
return emplace_impl<index_of<T>>( list, SWL_FWD(args)... );
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==================================== value status (20.7.3.6)
|
||
|
|
|
||
|
|
constexpr bool valueless_by_exception() const noexcept {
|
||
|
|
if constexpr ( can_be_valueless )
|
||
|
|
return current == npos;
|
||
|
|
else return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr index_type index() const noexcept {
|
||
|
|
return current;
|
||
|
|
}
|
||
|
|
|
||
|
|
// =================================== swap (20.7.3.7)
|
||
|
|
|
||
|
|
constexpr void swap(variant& o)
|
||
|
|
noexcept ( (std::is_nothrow_move_constructible_v<Ts> && ...)
|
||
|
|
&& (vimpl::swap_trait::template nothrow<Ts> && ...) )
|
||
|
|
requires (has_move_ctor && (vimpl::swap_trait::template able<Ts> && ...))
|
||
|
|
{
|
||
|
|
|
||
|
|
if constexpr (can_be_valueless){
|
||
|
|
// if one is valueless, move the element form the non-empty variant,
|
||
|
|
// reset it, and set it to valueless
|
||
|
|
constexpr auto impl_one_valueless = [] (auto&& full, auto& empty) {
|
||
|
|
vimpl::visit_with_index( SWL_FWD(full), vimpl::emplace_no_dtor_from_elem<variant&>{empty} );
|
||
|
|
full.reset_no_check();
|
||
|
|
full.current = npos;
|
||
|
|
};
|
||
|
|
|
||
|
|
switch( static_cast<int>(this->index() == npos) + static_cast<int>(o.index() == npos) * 2 )
|
||
|
|
{
|
||
|
|
case 0 :
|
||
|
|
break;
|
||
|
|
case 1 :
|
||
|
|
// "this" is valueless
|
||
|
|
impl_one_valueless( SWL_MOV(o), *this );
|
||
|
|
return;
|
||
|
|
case 2 :
|
||
|
|
// "other" is valueless
|
||
|
|
impl_one_valueless( SWL_MOV(*this), o );
|
||
|
|
return;
|
||
|
|
case 3 :
|
||
|
|
// both are valueless, do nothing
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
DebugAssert( not (valueless_by_exception() && o.valueless_by_exception()) );
|
||
|
|
|
||
|
|
vimpl::visit_with_index( o, [&o, this] (auto&& elem, auto index_) {
|
||
|
|
|
||
|
|
if (this->index() == index_){
|
||
|
|
using std::swap;
|
||
|
|
swap( this->unsafe_get<index_>(), elem );
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
using idx_t = decltype(index_);
|
||
|
|
vimpl::visit_with_index(*this, [this, &o, &elem] (auto&& this_elem, auto this_index) {
|
||
|
|
|
||
|
|
auto tmp { SWL_MOV(this_elem) };
|
||
|
|
|
||
|
|
// destruct the element
|
||
|
|
vimpl::destruct<alternative<this_index>>(this_elem);
|
||
|
|
|
||
|
|
// ok, we just destroyed the element in this, don't call the dtor again
|
||
|
|
this->emplace_no_dtor<idx_t::value>( SWL_MOV(elem) );
|
||
|
|
|
||
|
|
// we could refactor this
|
||
|
|
vimpl::destruct<alternative<idx_t::value>>(elem);
|
||
|
|
o.template emplace_no_dtor< (unsigned)(this_index) >( SWL_MOV(tmp) );
|
||
|
|
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// +================================== methods for internal use
|
||
|
|
// these methods performs no errors checking at all
|
||
|
|
|
||
|
|
template <vimpl::union_index_t Idx>
|
||
|
|
constexpr auto& unsafe_get() & noexcept {
|
||
|
|
static_assert(Idx < size);
|
||
|
|
DebugAssert(current == Idx);
|
||
|
|
return storage.template get<Idx>();
|
||
|
|
}
|
||
|
|
|
||
|
|
template <vimpl::union_index_t Idx>
|
||
|
|
constexpr auto&& unsafe_get() && noexcept {
|
||
|
|
static_assert(Idx < size);
|
||
|
|
DebugAssert(current == Idx);
|
||
|
|
return SWL_MOV( storage.template get<Idx>() );
|
||
|
|
}
|
||
|
|
|
||
|
|
template <vimpl::union_index_t Idx>
|
||
|
|
constexpr const auto& unsafe_get() const & noexcept {
|
||
|
|
static_assert(Idx < size);
|
||
|
|
DebugAssert(current == Idx);
|
||
|
|
return const_cast<variant&>(*this).unsafe_get<Idx>();
|
||
|
|
}
|
||
|
|
|
||
|
|
template <vimpl::union_index_t Idx>
|
||
|
|
constexpr const auto&& unsafe_get() const && noexcept {
|
||
|
|
static_assert(Idx < size);
|
||
|
|
DebugAssert(current == Idx);
|
||
|
|
return SWL_MOV(unsafe_get<Idx>());
|
||
|
|
}
|
||
|
|
|
||
|
|
private :
|
||
|
|
|
||
|
|
// assign from another variant
|
||
|
|
template <class Other, class Fn>
|
||
|
|
constexpr void assign_from(Other&& o, Fn&& fn){
|
||
|
|
if constexpr (can_be_valueless){
|
||
|
|
if (o.index() == npos){
|
||
|
|
if (current != npos){
|
||
|
|
reset_no_check();
|
||
|
|
current = npos;
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
DebugAssert(not o.valueless_by_exception());
|
||
|
|
vimpl::visit_with_index( SWL_FWD(o), SWL_FWD(fn) );
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned Idx, class... Args>
|
||
|
|
constexpr auto& emplace_impl(Args&&... args)
|
||
|
|
{
|
||
|
|
reset();
|
||
|
|
emplace_no_dtor<Idx>( SWL_FWD(args)... );
|
||
|
|
return unsafe_get<Idx>();
|
||
|
|
}
|
||
|
|
|
||
|
|
// can be used directly only when the variant is in a known empty state
|
||
|
|
template <unsigned Idx, class... Args>
|
||
|
|
constexpr void emplace_no_dtor(Args&&... args)
|
||
|
|
{
|
||
|
|
using T = alternative<Idx>;
|
||
|
|
|
||
|
|
if constexpr ( not std::is_nothrow_constructible_v<T, Args&&...> )
|
||
|
|
{
|
||
|
|
if constexpr ( std::is_nothrow_move_constructible_v<T> )
|
||
|
|
do_emplace_no_dtor<Idx>( T{SWL_FWD(args)...} );
|
||
|
|
else if constexpr ( std::is_nothrow_copy_constructible_v<T> )
|
||
|
|
{
|
||
|
|
T tmp {SWL_FWD(args)...};
|
||
|
|
do_emplace_no_dtor<Idx>( tmp );
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
static_assert( can_be_valueless && (Idx == Idx),
|
||
|
|
"Internal error : the possibly valueless branch of emplace was taken despite |can_be_valueless| being false");
|
||
|
|
current = npos;
|
||
|
|
do_emplace_no_dtor<Idx>( SWL_FWD(args)... );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
do_emplace_no_dtor<Idx>( SWL_FWD(args)... );
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned Idx, class... Args>
|
||
|
|
constexpr void do_emplace_no_dtor(Args&&... args)
|
||
|
|
{
|
||
|
|
auto* ptr = vimpl::addressof( unsafe_get<Idx>() );
|
||
|
|
|
||
|
|
#ifdef SWL_VARIANT_NO_CONSTEXPR_EMPLACE
|
||
|
|
using T = alternative<Idx>;
|
||
|
|
new ((void*)(ptr)) T ( SWL_FWD(args)... );
|
||
|
|
#else
|
||
|
|
std::construct_at( ptr, SWL_FWD(args)... );
|
||
|
|
#endif
|
||
|
|
|
||
|
|
current = static_cast<index_type>(Idx);
|
||
|
|
}
|
||
|
|
|
||
|
|
// destroy the current elem IFF not valueless
|
||
|
|
constexpr void reset() {
|
||
|
|
if constexpr (can_be_valueless)
|
||
|
|
if (valueless_by_exception()) return;
|
||
|
|
reset_no_check();
|
||
|
|
}
|
||
|
|
|
||
|
|
// destroy the current element without checking for valueless
|
||
|
|
constexpr void reset_no_check(){
|
||
|
|
DebugAssert( index() < size );
|
||
|
|
if constexpr ( not trivial_dtor ){
|
||
|
|
vimpl::visit_with_index( *this, [] (auto& elem, auto index_) {
|
||
|
|
vimpl::destruct<alternative<index_>>(elem);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// construct this from another variant, for constructors only
|
||
|
|
template <class Other>
|
||
|
|
constexpr void construct_from(Other&& o){
|
||
|
|
if constexpr (can_be_valueless)
|
||
|
|
if (o.valueless_by_exception()){
|
||
|
|
current = npos;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
vimpl::visit_with_index( SWL_FWD(o), vimpl::emplace_no_dtor_from_elem<variant&>{*this} );
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
friend struct vimpl::emplace_no_dtor_from_elem;
|
||
|
|
|
||
|
|
storage_t storage;
|
||
|
|
index_type current;
|
||
|
|
};
|
||
|
|
|
||
|
|
// ================================= value access (20.7.5)
|
||
|
|
|
||
|
|
template <class T, class... Ts>
|
||
|
|
constexpr bool holds_alternative(const variant<Ts...>& v) noexcept {
|
||
|
|
static_assert( (std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant" );
|
||
|
|
constexpr auto Index = variant<Ts...>::template index_of<T>;
|
||
|
|
return v.index() == Index;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========= get by index
|
||
|
|
|
||
|
|
template <std::size_t Idx, class... Ts>
|
||
|
|
constexpr auto& get (variant<Ts...>& v){
|
||
|
|
static_assert( Idx < sizeof...(Ts), "Index exceeds the variant size. ");
|
||
|
|
if (v.index() != Idx) throw bad_variant_access{"swl::variant : Bad variant access in get."};
|
||
|
|
return (v.template unsafe_get<Idx>());
|
||
|
|
}
|
||
|
|
|
||
|
|
template <std::size_t Idx, class... Ts>
|
||
|
|
constexpr const auto& get (const variant<Ts...>& v){
|
||
|
|
return swl::get<Idx>(const_cast<variant<Ts...>&>(v));
|
||
|
|
}
|
||
|
|
|
||
|
|
template <std::size_t Idx, class... Ts>
|
||
|
|
constexpr auto&& get (variant<Ts...>&& v){
|
||
|
|
return SWL_MOV( swl::get<Idx>(v) );
|
||
|
|
}
|
||
|
|
|
||
|
|
template <std::size_t Idx, class... Ts>
|
||
|
|
constexpr const auto&& get (const variant<Ts...>&& v){
|
||
|
|
return SWL_MOV( swl::get<Idx>(v) );
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========= get by type
|
||
|
|
|
||
|
|
template <class T, class... Ts>
|
||
|
|
constexpr T& get (variant<Ts...>& v){
|
||
|
|
return swl::get< variant<Ts...>::template index_of<T> >(v);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class... Ts>
|
||
|
|
constexpr const T& get (const variant<Ts...>& v){
|
||
|
|
return swl::get< variant<Ts...>::template index_of<T> >(v);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class... Ts>
|
||
|
|
constexpr T&& get (variant<Ts...>&& v){
|
||
|
|
return swl::get< variant<Ts...>::template index_of<T> >( SWL_FWD(v) );
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class... Ts>
|
||
|
|
constexpr const T&& get (const variant<Ts...>&& v){
|
||
|
|
return swl::get< variant<Ts...>::template index_of<T> >( SWL_FWD(v) );
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===== get_if by index
|
||
|
|
|
||
|
|
template <std::size_t Idx, class... Ts>
|
||
|
|
constexpr const auto* get_if(const variant<Ts...>* v) noexcept {
|
||
|
|
using rtype = typename variant<Ts...>::template alternative<Idx>*;
|
||
|
|
if (v == nullptr || v->index() != Idx)
|
||
|
|
return rtype{nullptr};
|
||
|
|
else
|
||
|
|
return vimpl::addressof( v->template unsafe_get<Idx>() );
|
||
|
|
}
|
||
|
|
|
||
|
|
template <std::size_t Idx, class... Ts>
|
||
|
|
constexpr auto* get_if(variant<Ts...>* v) noexcept {
|
||
|
|
using rtype = typename variant<Ts...>::template alternative<Idx>;
|
||
|
|
return const_cast<rtype*>(
|
||
|
|
swl::get_if<Idx>( static_cast<const variant<Ts...>*>(v) )
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ====== get_if by type
|
||
|
|
|
||
|
|
template <class T, class... Ts>
|
||
|
|
constexpr T* get_if(variant<Ts...>* v) noexcept {
|
||
|
|
static_assert( (std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant" );
|
||
|
|
return swl::get_if< variant<Ts...>::template index_of<T> >(v);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class... Ts>
|
||
|
|
constexpr const T* get_if(const variant<Ts...>* v) noexcept {
|
||
|
|
static_assert( (std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant" );
|
||
|
|
return swl::get_if< variant<Ts...>::template index_of<T> >(v);
|
||
|
|
}
|
||
|
|
|
||
|
|
// =============================== visitation (20.7.7)
|
||
|
|
|
||
|
|
template <class Fn, class... Vs>
|
||
|
|
constexpr decltype(auto) visit(Fn&& fn, Vs&&... vs){
|
||
|
|
if constexpr ( (std::decay_t<Vs>::can_be_valueless || ...) )
|
||
|
|
if ( (vs.valueless_by_exception() || ...) )
|
||
|
|
throw bad_variant_access{"swl::variant : Bad variant access in visit."};
|
||
|
|
|
||
|
|
if constexpr (sizeof...(Vs) == 1)
|
||
|
|
return vimpl::visit( SWL_FWD(fn), SWL_FWD(vs)...);
|
||
|
|
else
|
||
|
|
return vimpl::multi_visit( SWL_FWD(fn), SWL_FWD(vs)...);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class Fn>
|
||
|
|
constexpr decltype(auto) visit(Fn&& fn){
|
||
|
|
return SWL_FWD(fn)();
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class R, class Fn, class... Vs>
|
||
|
|
requires (is_variant<Vs> && ...)
|
||
|
|
constexpr R visit(Fn&& fn, Vs&&... vars){
|
||
|
|
return static_cast<R>( swl::visit( SWL_FWD(fn), SWL_FWD(vars)...) );
|
||
|
|
}
|
||
|
|
|
||
|
|
// ============================== relational operators (20.7.6)
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
requires ( vimpl::has_eq_comp<Ts> && ... )
|
||
|
|
constexpr bool operator==(const variant<Ts...>& v1, const variant<Ts...>& v2){
|
||
|
|
if (v1.index() != v2.index())
|
||
|
|
return false;
|
||
|
|
if constexpr (variant<Ts...>::can_be_valueless)
|
||
|
|
if (v1.valueless_by_exception()) return true;
|
||
|
|
return vimpl::visit_with_index( v2, [&v1] (auto& elem, auto index) -> bool {
|
||
|
|
return (v1.template unsafe_get<index>() == elem);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
constexpr bool operator!=(const variant<Ts...>& v1, const variant<Ts...>& v2)
|
||
|
|
requires requires { v1 == v2; }
|
||
|
|
{
|
||
|
|
return not(v1 == v2);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
requires ( vimpl::has_lesser_comp<const Ts&> && ... )
|
||
|
|
constexpr bool operator<(const variant<Ts...>& v1, const variant<Ts...>& v2){
|
||
|
|
if constexpr (variant<Ts...>::can_be_valueless){
|
||
|
|
if (v2.valueless_by_exception()) return false;
|
||
|
|
if (v1.valueless_by_exception()) return true;
|
||
|
|
}
|
||
|
|
if ( v1.index() == v2.index() ){
|
||
|
|
return vimpl::visit_with_index( v1, [&v2] (auto& elem, auto index) -> bool {
|
||
|
|
return (elem < v2.template unsafe_get<index>());
|
||
|
|
} );
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return (v1.index() < v2.index());
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
constexpr bool operator>(const variant<Ts...>& v1, const variant<Ts...>& v2)
|
||
|
|
requires requires { v2 < v1; }
|
||
|
|
{
|
||
|
|
return v2 < v1;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
requires ( vimpl::has_less_or_eq_comp<const Ts&> && ... )
|
||
|
|
constexpr bool operator<=(const variant<Ts...>& v1, const variant<Ts...>& v2){
|
||
|
|
if constexpr (variant<Ts...>::can_be_valueless){
|
||
|
|
if (v1.valueless_by_exception()) return true;
|
||
|
|
if (v2.valueless_by_exception()) return false;
|
||
|
|
}
|
||
|
|
if ( v1.index() == v2.index() ){
|
||
|
|
return vimpl::visit_with_index( v1, [&v2] (auto& elem, auto index) -> bool {
|
||
|
|
return (elem <= v2.template unsafe_get<index>());
|
||
|
|
});
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return (v1.index() < v2.index());
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
constexpr bool operator>=(const variant<Ts...>& v1, const variant<Ts...>& v2)
|
||
|
|
requires requires { v2 <= v1; }
|
||
|
|
{
|
||
|
|
return v2 <= v1;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================================== monostate (20.7.8, 20.7.9)
|
||
|
|
|
||
|
|
struct monostate{};
|
||
|
|
constexpr bool operator==(monostate, monostate) noexcept { return true; }
|
||
|
|
constexpr bool operator> (monostate, monostate) noexcept { return false; }
|
||
|
|
constexpr bool operator< (monostate, monostate) noexcept { return false; }
|
||
|
|
constexpr bool operator<=(monostate, monostate) noexcept { return true; }
|
||
|
|
constexpr bool operator>=(monostate, monostate) noexcept { return true; }
|
||
|
|
|
||
|
|
// ===================================== specialized algorithms (20.7.10)
|
||
|
|
|
||
|
|
template <class... Ts>
|
||
|
|
constexpr void swap(variant<Ts...>& a, variant<Ts...>& b)
|
||
|
|
noexcept (noexcept(a.swap(b)))
|
||
|
|
requires requires { a.swap(b); }
|
||
|
|
{
|
||
|
|
a.swap(b);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================================== helper classes (20.7.4)
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
requires is_variant<T>
|
||
|
|
inline constexpr std::size_t variant_size_v = std::decay_t<T>::size;
|
||
|
|
|
||
|
|
// not sure why anyone would need this, i'm adding it anyway
|
||
|
|
template <class T>
|
||
|
|
requires is_variant<T>
|
||
|
|
struct variant_size : std::integral_constant<std::size_t, variant_size_v<T>> {};
|
||
|
|
|
||
|
|
namespace vimpl {
|
||
|
|
// ugh, we have to take care of volatile here
|
||
|
|
template <bool IsVolatile>
|
||
|
|
struct var_alt_impl {
|
||
|
|
template <std::size_t Idx, class T>
|
||
|
|
using type = std::remove_reference_t<decltype( std::declval<T>().template unsafe_get<Idx>() )>;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <>
|
||
|
|
struct var_alt_impl<true> {
|
||
|
|
template <std::size_t Idx, class T>
|
||
|
|
using type = volatile typename var_alt_impl<false>::template type<Idx, std::remove_volatile_t<T>>;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
template <std::size_t Idx, class T>
|
||
|
|
requires (Idx < variant_size_v<T>)
|
||
|
|
using variant_alternative_t = typename vimpl::var_alt_impl< std::is_volatile_v<T> >::template type<Idx, T>;
|
||
|
|
|
||
|
|
template <std::size_t Idx, class T>
|
||
|
|
requires is_variant<T>
|
||
|
|
struct variant_alternative {
|
||
|
|
using type = variant_alternative_t<Idx, T>;
|
||
|
|
};
|
||
|
|
|
||
|
|
// ===================================== extensions (unsafe_get)
|
||
|
|
|
||
|
|
template <std::size_t Idx, class Var>
|
||
|
|
requires is_variant<Var>
|
||
|
|
constexpr auto&& unsafe_get(Var&& var) noexcept {
|
||
|
|
static_assert( Idx < std::decay_t<Var>::size, "Index exceeds the variant size." );
|
||
|
|
return SWL_FWD(var).template unsafe_get<Idx>();
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class Var>
|
||
|
|
requires is_variant<Var>
|
||
|
|
constexpr auto&& unsafe_get(Var&& var) noexcept {
|
||
|
|
return swl::unsafe_get< std::decay_t<Var>::template index_of<T> >( SWL_FWD(var) );
|
||
|
|
}
|
||
|
|
|
||
|
|
} // SWL
|
||
|
|
|
||
|
|
// ====================================== hash support (20.7.12)
|
||
|
|
#ifndef SWL_VARIANT_NO_STD_HASH
|
||
|
|
|
||
|
|
namespace std {
|
||
|
|
template <class... Ts>
|
||
|
|
requires (swl::vimpl::has_std_hash<Ts> && ...)
|
||
|
|
struct hash<swl::variant<Ts...>> {
|
||
|
|
std::size_t operator()(const swl::variant<Ts...>& v) const {
|
||
|
|
if constexpr ( swl::variant<Ts...>::can_be_valueless )
|
||
|
|
if (v.valueless_by_exception()) return -1;
|
||
|
|
|
||
|
|
return swl::vimpl::visit_with_index( v, [] (auto& elem, auto index_) {
|
||
|
|
using type = std::remove_cvref_t<decltype(elem)>;
|
||
|
|
return std::hash<type>{}(elem) + index_;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <>
|
||
|
|
struct hash<swl::monostate> {
|
||
|
|
constexpr std::size_t operator()(swl::monostate) const noexcept { return -1; }
|
||
|
|
};
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
#undef SWL_VARIANT_NO_STD_HASH
|
||
|
|
#endif // std-hash
|
||
|
|
|
||
|
|
#undef DebugAssert
|
||
|
|
#undef SWL_FWD
|
||
|
|
#undef SWL_MOV
|
||
|
|
|
||
|
|
#ifdef SWL_VARIANT_NO_CONSTEXPR_EMPLACE
|
||
|
|
#undef SWL_VARIANT_NO_CONSTEXPR_EMPLACE
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#endif // eof
|