728 lines
25 KiB
C++
728 lines
25 KiB
C++
//-------------------------------------
|
|
// utils::variant: Tagged union in C++
|
|
//-------------------------------------
|
|
//
|
|
// Copyright kennytm (auraHT Ltd.) 2011.
|
|
|
|
// Boost Software License - Version 1.0 - August 17th, 2003
|
|
|
|
// Permission is hereby granted, free of charge, to any person or organization
|
|
// obtaining a copy of the software and accompanying documentation covered by
|
|
// this license (the "Software") to use, reproduce, display, distribute,
|
|
// execute, and transmit the Software, and to prepare derivative works of the
|
|
// Software, and to permit third-parties to whom the Software is furnished to
|
|
// do so, all subject to the following:
|
|
|
|
// The copyright notices in the Software and this entire statement, including
|
|
// the above license grant, this restriction and the following disclaimer,
|
|
// must be included in all copies of the Software, in whole or in part, and
|
|
// all derivative works of the Software, unless such copies or derivative
|
|
// works are solely in the form of machine-executable object code generated by
|
|
// a source language processor.
|
|
|
|
// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
/**
|
|
|
|
``<utils/variant.hpp>`` --- Tagged union
|
|
========================================
|
|
|
|
This module provides the :type:`utils::variant` type, which stores tagged union
|
|
of objects (also known as discriminated union or sum type).
|
|
|
|
This is mainly a rewrite of `boost::variant
|
|
<http://www.boost.org/doc/libs/1_48_0/doc/html/variant.html>`_ for C++11 support.
|
|
|
|
:type:`utils::variant` support the following features over ``boost::variant``:
|
|
|
|
* Proper move semantics
|
|
* Stricter, compile-time type checking
|
|
* Visitation using pattern-matching, in additional to the traditional
|
|
class-based method.
|
|
|
|
but it also has some features not present in :type:`utils::variant`, and will
|
|
likely not be supported here:
|
|
|
|
* Recursive variant
|
|
* Reference members
|
|
* `Boost.MPL <http://www.boost.org/doc/libs/1_47_0/libs/mpl/doc/index.html>`_
|
|
* C++03, and support for compilers other than gcc ≥4.7 (and clang when its C++11
|
|
support becomes better).
|
|
|
|
Synopsis
|
|
--------
|
|
|
|
::
|
|
|
|
#include <cstdio>
|
|
#include <vector>
|
|
#include <utils/variant.hpp>
|
|
|
|
int main()
|
|
{
|
|
std::vector<int> v {8, 9, 10, 12, 18, 24, 36, 48, 64, 72, 96};
|
|
|
|
utils::variant<std::vector<int>, std::string> u = std::move(v);
|
|
|
|
assert(v.is_type< std::vector<int> >());
|
|
|
|
utills::case_of(u,
|
|
[](const std::vector<int>& vec)
|
|
{
|
|
for (int val : vec)
|
|
printf("%d\n", val);
|
|
},
|
|
[](const std::string& str)
|
|
{
|
|
printf("[%s]\n", str.c_str());
|
|
}
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
|
|
*/
|
|
|
|
#ifndef VARIANT_HPP_XMPYUK9CBNN
|
|
#define VARIANT_HPP_XMPYUK9CBNN
|
|
|
|
#include <utility>
|
|
#include <type_traits>
|
|
#include <iosfwd>
|
|
#if !defined(BOOST_NO_TYPEID)
|
|
#include <typeinfo>
|
|
#endif
|
|
#include "traits.hpp"
|
|
|
|
namespace utils {
|
|
|
|
template <typename RT = void>
|
|
class static_visitor
|
|
{
|
|
public:
|
|
typedef RT result_type;
|
|
};
|
|
|
|
template <typename... T>
|
|
class variant;
|
|
|
|
template <typename T>
|
|
struct is_variant { enum { value = false }; };
|
|
template <typename... T>
|
|
struct is_variant<variant<T...>> { enum { value = true }; };
|
|
}
|
|
|
|
#include "variant-impl.hpp"
|
|
|
|
namespace utils {
|
|
|
|
/**
|
|
Members
|
|
-------
|
|
*/
|
|
|
|
/**
|
|
.. type:: class utils::variant<T...> final
|
|
:default_constructible:
|
|
:movable:
|
|
:copyable:
|
|
|
|
The tagged union type.
|
|
*/
|
|
template <typename... T>
|
|
class variant
|
|
{
|
|
typedef xx_impl::union_impl<T...> union_type;
|
|
|
|
size_t _index;
|
|
union_type _storage;
|
|
|
|
class init_to_visitor : public static_visitor<void>
|
|
{
|
|
public:
|
|
variant<T...>* this_;
|
|
|
|
template <typename U>
|
|
void operator()(U&& other)
|
|
{
|
|
this_->init(std::forward<U>(other));
|
|
}
|
|
};
|
|
|
|
template <typename U>
|
|
void init(U&& other)
|
|
{
|
|
typedef xx_impl::get_index<U, xx_impl::is_constructible, T...> index_tmpl;
|
|
static_assert(index_tmpl::found, "Constructing variant from unexpected type.");
|
|
static_assert(!index_tmpl::ambiguous, "Constructing variant with ambiguous conversion.");
|
|
static const size_t index_of_U = index_tmpl::index;
|
|
_index = index_of_U;
|
|
|
|
xx_impl::init_visitor_1<U> ctor (std::forward<U>(other));
|
|
xx_impl::static_applier<index_of_U>()(_storage, ctor);
|
|
}
|
|
|
|
public:
|
|
/**
|
|
.. function:: variant()
|
|
|
|
Construct a new variant type and initialize with the default value
|
|
of the first type in *T*.
|
|
*/
|
|
variant() : _index(0)
|
|
{
|
|
new(&_storage.head) decltype(_storage.head);
|
|
}
|
|
|
|
~variant()
|
|
{
|
|
xx_impl::destroy_visitor dtor;
|
|
apply(_storage, _index, dtor);
|
|
}
|
|
|
|
template <typename U>
|
|
variant(U&& other)
|
|
{
|
|
this->init(std::forward<U>(other));
|
|
}
|
|
|
|
template <typename... TOther>
|
|
variant(variant<TOther...>& other)
|
|
{
|
|
init_to_visitor ctor;
|
|
ctor.this_ = this;
|
|
apply(other._storage, other._index, ctor);
|
|
}
|
|
|
|
template <typename... TOther>
|
|
variant(variant<TOther...>&& other)
|
|
{
|
|
init_to_visitor ctor;
|
|
ctor.this_ = this;
|
|
apply(std::move(other._storage), other._index, ctor);
|
|
}
|
|
|
|
variant(variant<T...>& other) : _index(other._index)
|
|
{
|
|
xx_impl::init_visitor_2 ctor;
|
|
apply(_storage, other._storage, _index, ctor);
|
|
}
|
|
|
|
variant(const variant<T...>& other) : _index(other._index)
|
|
{
|
|
xx_impl::init_visitor_2 ctor;
|
|
apply(_storage, other._storage, _index, ctor);
|
|
}
|
|
|
|
variant(variant<T...>&& other) : _index(other._index)
|
|
{
|
|
xx_impl::init_visitor_2 ctor;
|
|
apply(_storage, std::move(other._storage), _index, ctor);
|
|
}
|
|
|
|
private:
|
|
template <typename CA>
|
|
void perform_safe_copy(bool nothrow_movable, CA copy_action)
|
|
{
|
|
xx_impl::destroy_visitor dtor;
|
|
|
|
if (nothrow_movable)
|
|
{
|
|
apply(_storage, _index, dtor);
|
|
copy_action();
|
|
}
|
|
else
|
|
{
|
|
struct backup_owner
|
|
{
|
|
size_t index;
|
|
union_type* storage;
|
|
|
|
constexpr bool can_restore() const noexcept { return index < sizeof...(T); }
|
|
|
|
backup_owner() : index(sizeof...(T)), storage(new union_type) {}
|
|
~backup_owner()
|
|
{
|
|
if (storage != nullptr)
|
|
{
|
|
if (this->can_restore())
|
|
{
|
|
xx_impl::destroy_visitor dtor;
|
|
apply(*storage, index, dtor);
|
|
}
|
|
delete storage;
|
|
}
|
|
}
|
|
} backup;
|
|
|
|
xx_impl::init_visitor_2 ctor2;
|
|
xx_impl::is_nothrow_movable_checker nothrow_movable_checker;
|
|
|
|
try
|
|
{
|
|
if (xx_impl::apply(_storage, _index, nothrow_movable_checker))
|
|
xx_impl::apply(*backup.storage, std::move(_storage), _index, ctor2);
|
|
else
|
|
xx_impl::apply(*backup.storage, _storage, _index, ctor2);
|
|
backup.index = _index;
|
|
|
|
xx_impl::apply(_storage, _index, dtor);
|
|
copy_action();
|
|
}
|
|
catch (...)
|
|
{
|
|
if (backup.can_restore())
|
|
{
|
|
if (xx_impl::apply(_storage, backup.index, nothrow_movable_checker))
|
|
xx_impl::apply(_storage, std::move(*backup.storage), backup.index, ctor2);
|
|
else
|
|
xx_impl::apply(_storage, *backup.storage, backup.index, ctor2);
|
|
}
|
|
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
template <typename U>
|
|
variant<T...>& operator=(U&& other) noexcept(xx_impl::is_generic_nothrow_assignable<U>())
|
|
{
|
|
typedef xx_impl::get_index<U, xx_impl::is_assignable, T...> index_tmpl;
|
|
static_assert(index_tmpl::found, "Assigning to variant from unexpected type.");
|
|
static_assert(!index_tmpl::ambiguous, "Assigning to variant with ambiguous conversion.");
|
|
static const size_t index_of_U = index_tmpl::index;
|
|
|
|
if (_index == index_of_U)
|
|
{
|
|
xx_impl::assign_visitor_1<U> copier (std::forward<U>(other));
|
|
xx_impl::static_applier<index_of_U>()(_storage, copier);
|
|
}
|
|
else
|
|
{
|
|
xx_impl::init_visitor_1<U> ctor (std::forward<U>(other));
|
|
xx_impl::static_applier<index_of_U> static_applier;
|
|
this->perform_safe_copy(xx_impl::is_generic_nothrow_assignable<U>(),
|
|
[&, this] { static_applier(_storage, ctor); }
|
|
);
|
|
_index = index_of_U;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename... TOther>
|
|
typename std::enable_if<!xx_impl::get_index<variant<TOther...>,
|
|
xx_impl::is_same,
|
|
T...>::is_exact,
|
|
variant<T...>>::type&
|
|
operator=(variant<TOther...>& other)
|
|
{
|
|
xx_impl::is_same_visitor is_same;
|
|
xx_impl::is_one_of_visitor<T...> has_same;
|
|
|
|
auto same_type_pair = xx_impl::apply2(_storage, _index, other._storage, other._index, is_same);
|
|
bool has_same_type = xx_impl::apply(other._storage, other._index, has_same);
|
|
|
|
if (same_type_pair.first || (!has_same_type && same_type_pair.second))
|
|
{
|
|
xx_impl::assign_visitor_2 assigner;
|
|
xx_impl::apply2(_storage, _index, other._storage, other._index, assigner);
|
|
}
|
|
else
|
|
{
|
|
xx_impl::assign_to_visitor<variant<T...>> assigner (*this);
|
|
xx_impl::apply(other._storage, other._index, assigner);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename... TOther>
|
|
variant<T...>& operator=(variant<TOther...>&& other)
|
|
{
|
|
xx_impl::is_same_visitor is_same;
|
|
xx_impl::is_one_of_visitor<T...> has_same;
|
|
|
|
auto same_type_pair = xx_impl::apply2(_storage, _index, other._storage, other._storage, is_same);
|
|
bool has_same_type = xx_impl::apply(other._storage, other._index, has_same);
|
|
|
|
if (same_type_pair.first || (!has_same_type && same_type_pair.second))
|
|
{
|
|
xx_impl::assign_visitor_2 assigner;
|
|
xx_impl::apply2(_storage, _index, std::move(other._storage), other._index, assigner);
|
|
}
|
|
else
|
|
{
|
|
xx_impl::assign_to_visitor<variant<T...>> assigner (*this);
|
|
xx_impl::apply(std::move(other._storage), other._index, assigner);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
variant<T...>& operator=(const variant<T...>& other)
|
|
{
|
|
if (_index == other._index)
|
|
{
|
|
xx_impl::assign_visitor_2 assigner;
|
|
xx_impl::apply(_storage, other._storage, _index, assigner);
|
|
}
|
|
else
|
|
{
|
|
xx_impl::is_nothrow_copyable_checker cc;
|
|
bool is_nothrow_copyable = xx_impl::apply(other._storage, other._index, cc);
|
|
|
|
this->perform_safe_copy(is_nothrow_copyable,
|
|
[=, &other] {
|
|
xx_impl::init_visitor_2 ctor;
|
|
xx_impl::apply(_storage, other._storage, other._index, ctor);
|
|
}
|
|
);
|
|
_index = other._index;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
variant<T...>& operator=(variant<T...>& other)
|
|
{
|
|
const auto& other_const = other;
|
|
return (*this = other_const);
|
|
}
|
|
|
|
variant<T...>& operator=(variant<T...>&& other)
|
|
{
|
|
if (_index == other._index)
|
|
{
|
|
xx_impl::assign_visitor_2 assigner;
|
|
apply(_storage, std::move(other._storage), _index, assigner);
|
|
}
|
|
else
|
|
{
|
|
xx_impl::is_nothrow_movable_checker mc;
|
|
bool is_nothrow_movable = xx_impl::apply(other._storage, other._index, mc);
|
|
|
|
this->perform_safe_copy(is_nothrow_movable,
|
|
[=, &other] {
|
|
xx_impl::init_visitor_2 ctor;
|
|
xx_impl::apply(_storage, std::move(other._storage), other._index, ctor);
|
|
}
|
|
);
|
|
_index = other._index;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void swap(variant& other)
|
|
{
|
|
if (_index == other._index)
|
|
{
|
|
xx_impl::swap_visitor_2 swapper;
|
|
xx_impl::apply(_storage, other._storage, _index, swapper);
|
|
}
|
|
else
|
|
{
|
|
auto tmp = std::move(other);
|
|
other = std::move(*this);
|
|
*this = std::move(tmp);
|
|
}
|
|
}
|
|
|
|
template <typename U>
|
|
bool operator==(const U& other) const
|
|
{
|
|
typedef xx_impl::get_index<U, xx_impl::is_equatable, T...> index_tmpl;
|
|
static_assert(index_tmpl::found, "Equating variant to unexpected type.");
|
|
static_assert(!index_tmpl::ambiguous, "Equating variant with ambiguous conversion.");
|
|
static const size_t index_of_U = index_tmpl::index;
|
|
|
|
if (index_of_U != _index)
|
|
return false;
|
|
|
|
xx_impl::equals_visitor_1<U> eq (other);
|
|
return xx_impl::static_applier<index_of_U>()(_storage, eq);
|
|
}
|
|
|
|
template <typename U>
|
|
bool operator<(const U& other) const
|
|
{
|
|
typedef xx_impl::get_index<U, xx_impl::is_less_than_comparable, T...> index_tmpl;
|
|
static_assert(index_tmpl::found, "Comparing variant with unexpected type.");
|
|
static_assert(!index_tmpl::ambiguous, "Comparing variant with ambiguous conversion.");
|
|
static const size_t index_of_U = index_tmpl::index;
|
|
|
|
if (index_of_U != _index)
|
|
return _index < index_of_U;
|
|
|
|
xx_impl::less_than_visitor_1<U> lt (other);
|
|
return xx_impl::static_applier<index_of_U>()(_storage, lt);
|
|
}
|
|
|
|
template <typename U>
|
|
bool operator>(const U& other) const
|
|
{
|
|
typedef xx_impl::get_index<U, xx_impl::is_greater_than_comparable, T...> index_tmpl;
|
|
static_assert(index_tmpl::found, "Comparing variant with unexpected type.");
|
|
static_assert(!index_tmpl::ambiguous, "Comparing variant with ambiguous conversion.");
|
|
static const size_t index_of_U = index_tmpl::index;
|
|
|
|
if (index_of_U != _index)
|
|
return _index > index_of_U;
|
|
|
|
xx_impl::greater_than_visitor_1<U> gt (other);
|
|
return xx_impl::static_applier<index_of_U>()(_storage, gt);
|
|
}
|
|
|
|
bool operator==(const variant<T...>& other) const
|
|
{
|
|
if (_index != other._index)
|
|
return false;
|
|
|
|
xx_impl::equals_visitor_2 eq;
|
|
return xx_impl::apply(_storage, other._storage, _index, eq);
|
|
}
|
|
|
|
bool operator<(const variant<T...>& other) const
|
|
{
|
|
if (_index != other._index)
|
|
return _index < other._index;
|
|
|
|
xx_impl::less_than_visitor_2 lt;
|
|
return xx_impl::apply(_storage, other._storage, _index, lt);
|
|
}
|
|
|
|
template <typename U>
|
|
bool operator!=(const U& a) const { return !(*this == a); }
|
|
template <typename U>
|
|
bool operator>=(const U& a) const { return !(*this < a); }
|
|
template <typename U>
|
|
bool operator<=(const U& a) const { return !(*this > a); }
|
|
|
|
#if !defined(BOOST_NO_TYPEID)
|
|
/**
|
|
.. function:: const std::type_info& type() const noexcept
|
|
|
|
Returns the typeid of the currently active member. This function
|
|
will not be available if you define BOOST_NO_TYPEID before including
|
|
this header.
|
|
*/
|
|
const std::type_info& type() const noexcept
|
|
{
|
|
xx_impl::typeid_visitor tv;
|
|
return xx_impl::apply(_storage, _index, tv);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
.. function:: bool is_type<U>() const noexcept
|
|
|
|
Check if the variant is currently containing the **exact type** *U*.
|
|
*/
|
|
template <typename U>
|
|
bool is_type() const noexcept
|
|
{
|
|
typedef xx_impl::get_index<U, xx_impl::is_same, T...> index_impl;
|
|
static_assert(index_impl::is_exact, "Checking from unexpected type.");
|
|
return index_impl::index == _index;
|
|
}
|
|
|
|
template <typename SV, typename V>
|
|
friend typename SV::result_type apply_visitor(SV& visitor, V&& variant);
|
|
|
|
template <typename SV, typename V1, typename V2>
|
|
friend typename SV::result_type apply_visitor(SV& visitor, V1&& variant1, V2&& variant2);
|
|
|
|
template <typename SV, typename V>
|
|
friend typename SV::result_type apply_visitor(SV&& visitor, V&& variant);
|
|
|
|
template <typename SV, typename V1, typename V2>
|
|
friend typename SV::result_type apply_visitor(SV&& visitor, V1&& variant1, V2&& variant2);
|
|
|
|
template <typename U, typename... TX>
|
|
friend U* get(variant<TX...>* v) noexcept;
|
|
|
|
template <typename U, typename... TX>
|
|
friend const U* get(const variant<TX...>* v) noexcept;
|
|
|
|
template <typename V, typename... F>
|
|
friend typename xx_impl::common_result_type<F...>::type case_of(V&& variant, F&&... functions);
|
|
|
|
template <typename...>
|
|
friend class variant;
|
|
};
|
|
|
|
/**
|
|
.. function:: VisitorType::result_type utils::apply_visitor<VisitorType, T...>(VisitorType visitor, const utils::variant<T...>& var)
|
|
VisitorType::result_type utils::apply_visitor<VisitorType, T...>(VisitorType visitor, utils::variant<T...>& var)
|
|
|
|
Perform an operation on the variant. The *visitor* must be a function
|
|
object that accepts every possible type of *T*, and has the same return
|
|
type for all overloads, which must be reported as
|
|
``VisitorType::result_type``. You could use the
|
|
:type:`~utils::static_visitor` to ensure this.
|
|
*/
|
|
template <typename SV, typename V>
|
|
typename SV::result_type apply_visitor(SV& visitor, V&& variant)
|
|
{
|
|
return xx_impl::apply(forward_like<V>(variant._storage), variant._index, visitor);
|
|
}
|
|
template <typename SV, typename V>
|
|
typename SV::result_type apply_visitor(SV&& visitor, V&& variant)
|
|
{
|
|
return xx_impl::apply(forward_like<V>(variant._storage), variant._index, visitor);
|
|
}
|
|
|
|
/**
|
|
.. function:: VisitorType::result_type utils::apply_visitor<VisitorType, T..., U...>(VisitorType visitor, const utils::variant<T...>& var1, const utils::variant<U...>& var2)
|
|
VisitorType::result_type utils::apply_visitor<VisitorType, T..., U...>(VisitorType visitor, utils::variant<T...>& var1, const utils::variant<U...>& var2)
|
|
VisitorType::result_type utils::apply_visitor<VisitorType, T..., U...>(VisitorType visitor, const utils::variant<T...>& var1, utils::variant<U...>& var2)
|
|
VisitorType::result_type utils::apply_visitor<VisitorType, T..., U...>(VisitorType visitor, utils::variant<T...>& var1, utils::variant<U...>& var2)
|
|
|
|
Perform double visitation on a pair of variants.
|
|
*/
|
|
template <typename SV, typename V1, typename V2>
|
|
typename SV::result_type apply_visitor(SV& visitor, V1&& variant1, V2&& variant2)
|
|
{
|
|
return xx_impl::apply2(variant1._storage, variant1._index,
|
|
variant2._storage, variant2._index, visitor);
|
|
}
|
|
template <typename SV, typename V1, typename V2>
|
|
typename SV::result_type apply_visitor(SV&& visitor, V1&& variant1, V2&& variant2)
|
|
{
|
|
return xx_impl::apply2(variant1._storage, variant1._index,
|
|
variant2._storage, variant2._index, visitor);
|
|
}
|
|
|
|
/**
|
|
.. function:: auto utils::apply_visitor<VisitorType>(VisitorType visitor)
|
|
|
|
Return a function object that will perform single or double visitation
|
|
when called. In Boost this is known as *delayed visitation*.
|
|
*/
|
|
template <typename SV>
|
|
xx_impl::delayed_visitor<SV> apply_visitor(SV& visitor)
|
|
{
|
|
return xx_impl::delayed_visitor<SV>(visitor);
|
|
}
|
|
|
|
/**
|
|
.. function:: auto utils::case_of<T..., F...>(const utils::variant<T...>& var, F&&... functions)
|
|
auto utils::case_of<T..., F...>(utils::variant<T...>& var, F&&... functions)
|
|
|
|
Apply one of the functions which has compatible argument type to the
|
|
variant, and return that result.
|
|
*/
|
|
template <typename V, typename... F>
|
|
typename xx_impl::common_result_type<F...>::type case_of(V&& variant, F&&... functions)
|
|
{
|
|
return xx_impl::apply_funcs_run(forward_like<V>(variant._storage),
|
|
variant._index,
|
|
std::forward<F>(functions)...);
|
|
}
|
|
|
|
/**
|
|
.. function:: U* utils::get<U, T...>(utils::variant<T...>* var_ptr) noexcept
|
|
const U* utils::get<U, T...>(const utils::variant<T...>* var_ptr) noexcept
|
|
|
|
Obtain a pointer to *U* if the variant's active member is really of type
|
|
*U*. Return ``nullptr`` if not.
|
|
*/
|
|
template <typename U, typename... T>
|
|
U* get(variant<T...>* v) noexcept
|
|
{
|
|
typedef xx_impl::get_index<U, xx_impl::is_same, T...> index_impl;
|
|
static_assert(index_impl::is_exact, "Getting from unexpected type.");
|
|
if (index_impl::index == v->_index)
|
|
{
|
|
xx_impl::getter_visitor<U> getter;
|
|
return xx_impl::static_applier<index_impl::index>()(v->_storage, getter);
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename U, typename... T>
|
|
const U* get(const variant<T...>* v) noexcept
|
|
{
|
|
typedef xx_impl::get_index<U, xx_impl::is_same, T...> index_impl;
|
|
static_assert(index_impl::is_exact, "Getting from unexpected type.");
|
|
if (index_impl::index == v->_index)
|
|
{
|
|
xx_impl::getter_visitor<const U> getter;
|
|
return xx_impl::static_applier<index_impl::index>()(v->_storage, getter);
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
.. type:: class utils::bad_get : public std::exception
|
|
|
|
This exception is thrown when trying to :func:`~utils::get` a reference
|
|
from a variant that does not have the required type.
|
|
*/
|
|
class bad_get : public std::exception {
|
|
public:
|
|
virtual const char* what() const noexcept { return "bad_get"; }
|
|
};
|
|
|
|
/**
|
|
.. function:: U& utils::get<U, T...>(utils::variant<T...>& var)
|
|
const U& utils::get<U, T...>(const utils::variant<T...>& var)
|
|
|
|
Obtain a reference to *U* if the variant's active member is really of
|
|
type *U*. Throws a :type:`~utils::bad_get` exception if not.
|
|
*/
|
|
template <typename U, typename... T>
|
|
U& get(variant<T...>& v)
|
|
{
|
|
auto res = get<U>(&v);
|
|
if (res == nullptr)
|
|
throw bad_get();
|
|
return *res;
|
|
}
|
|
|
|
template <typename U, typename... T>
|
|
const U& get(const variant<T...>& v)
|
|
{
|
|
auto res = get<U>(&v);
|
|
if (res == nullptr)
|
|
throw bad_get();
|
|
return *res;
|
|
}
|
|
|
|
template <typename U, typename... T>
|
|
bool operator==(const U& a, const variant<T...>& v) { return v == a; }
|
|
template <typename U, typename... T>
|
|
bool operator<(const U& a, const variant<T...>& v) { return v > a; }
|
|
template <typename U, typename... T>
|
|
bool operator>(const U& a, const variant<T...>& v) { return v < a; }
|
|
template <typename U, typename... T>
|
|
bool operator!=(const U& a, const variant<T...>& v) { return !(v == a); }
|
|
template <typename U, typename... T>
|
|
bool operator>=(const U& a, const variant<T...>& v) { return !(v > a); }
|
|
template <typename U, typename... T>
|
|
bool operator<=(const U& a, const variant<T...>& v) { return !(v < a); }
|
|
|
|
template <typename... T>
|
|
std::ostream& operator<<(std::ostream& stream, const variant<T...>& v)
|
|
{
|
|
xx_impl::ostream_visitor visitor (stream);
|
|
return apply_visitor(visitor, v);
|
|
}
|
|
}
|
|
|
|
namespace std
|
|
{
|
|
template <typename... T>
|
|
void swap(utils::variant<T...>& a, utils::variant<T...>& b)
|
|
{
|
|
a.swap(b);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|