forked from enlightenment/efl
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
347 lines
8.5 KiB
347 lines
8.5 KiB
#ifndef EINA_VARIANT_HH_ |
|
#define EINA_VARIANT_HH_ |
|
|
|
#include <cstddef> |
|
#include <algorithm> |
|
#include <utility> |
|
#include <type_traits> |
|
#include <tuple> |
|
|
|
#include <eina_aligned_union.hh> |
|
|
|
namespace efl { namespace eina { |
|
|
|
namespace _impl { |
|
|
|
template <typename T, typename U, typename...Others> |
|
struct is_one_of : std::conditional<std::is_same<T, U>::value |
|
, std::is_same<T, U> |
|
, is_one_of<T, Others...> >::type::type |
|
{}; |
|
|
|
template <typename T, typename U> |
|
struct is_one_of<T, U> : std::is_same<T, U> |
|
{}; |
|
|
|
|
|
template <std::size_t size, typename T, typename U, typename...Args> |
|
struct find_impl : find_impl<size+1, T, Args...> |
|
{}; |
|
|
|
template <std::size_t size, typename T, typename...Args> |
|
struct find_impl<size, T, T, Args...> : std::integral_constant<std::size_t, size> {}; |
|
|
|
template <typename T, typename U, typename...Args> |
|
struct find : find_impl<0u, T, U, Args...> |
|
{}; |
|
|
|
} |
|
|
|
template <std::size_t N, std::size_t L, typename Tuple> |
|
struct call_visitor |
|
{ |
|
template <typename F> |
|
static typename F::result_type call(int type, void const* buffer, F f) |
|
{ |
|
if(type == N) |
|
{ |
|
using std::tuple_element; |
|
typedef typename tuple_element<N, Tuple>::type type; |
|
type const* o = static_cast<type const*>(buffer); |
|
return f(*o); |
|
} |
|
else |
|
return call_visitor<N+1, L, Tuple>::call(type, buffer, f); |
|
} |
|
template <typename F> |
|
static typename F::result_type call(int type, void* buffer, F f) |
|
{ |
|
if(type == N) |
|
{ |
|
using std::tuple_element; |
|
typedef typename tuple_element<N, Tuple>::type type; |
|
type* o = static_cast<type*>(buffer); |
|
return f(*o); |
|
} |
|
else |
|
return call_visitor<N+1, L, Tuple>::call(type, buffer, f); |
|
} |
|
}; |
|
|
|
template <std::size_t L, typename Tuple> |
|
struct call_visitor<L, L, Tuple> |
|
{ |
|
template <typename F> |
|
static typename F::result_type call(int, void const*, F) |
|
{ |
|
std::abort(); |
|
} |
|
}; |
|
|
|
struct compare_equal_visitor |
|
{ |
|
void const* buffer; |
|
typedef bool result_type; |
|
template <typename T> |
|
bool operator()(T const& other) const |
|
{ |
|
return *static_cast<T const*>(buffer) == other; |
|
} |
|
}; |
|
|
|
struct copy_visitor |
|
{ |
|
typedef void result_type; |
|
void* buffer; |
|
template <typename T> |
|
void operator()(T const& other) const |
|
{ |
|
new (buffer) T(other); |
|
} |
|
}; |
|
|
|
struct move_visitor |
|
{ |
|
typedef void result_type; |
|
void* buffer; |
|
template <typename T> |
|
void operator()(T& other) const |
|
{ |
|
typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type; |
|
new (buffer) type(std::move(other)); |
|
} |
|
}; |
|
|
|
struct assign_visitor |
|
{ |
|
typedef void result_type; |
|
void* buffer; |
|
template <typename T> |
|
void operator()(T const& other) const |
|
{ |
|
typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type; |
|
type* assigned = static_cast<type*>(buffer); |
|
*assigned = other; |
|
} |
|
}; |
|
|
|
struct move_assign_visitor |
|
{ |
|
typedef void result_type; |
|
void* buffer; |
|
template <typename T> |
|
void operator()(T& other) const |
|
{ |
|
typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type; |
|
type* assigned = static_cast<type*>(buffer); |
|
*assigned = std::move(other); |
|
} |
|
}; |
|
|
|
struct destroy_visitor |
|
{ |
|
typedef void result_type; |
|
template <typename T> |
|
void operator()(T&& other) const noexcept |
|
{ |
|
typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type; |
|
other.~type(); |
|
} |
|
}; |
|
|
|
template <typename T> |
|
struct get_visitor |
|
{ |
|
typedef T* result_type; |
|
T* operator()(T& object) const |
|
{ |
|
return &object; |
|
} |
|
template <typename U> |
|
T* operator()(U&) const { return nullptr; } |
|
}; |
|
|
|
template <typename... Args> |
|
struct variant |
|
{ |
|
typedef variant<Args...> _self_type; /**< Type for the optional class itself. */ |
|
|
|
constexpr variant() |
|
: type(-1) |
|
{} |
|
|
|
template <typename T> |
|
variant(T object, |
|
typename std::enable_if<_impl::is_one_of |
|
<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Args...>::value>::type* = 0) |
|
: type(_impl::find<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Args...>::value) |
|
{ |
|
construct(object); |
|
} |
|
|
|
variant(variant const& other) |
|
: type(other.type) |
|
{ |
|
if(other.type != -1) |
|
other.visit(copy_visitor{static_cast<void*>(&buffer)}); |
|
} |
|
variant& operator=(variant const& other) |
|
{ |
|
if(type == other.type && type != -1) |
|
{ |
|
other.visit(assign_visitor{static_cast<void*>(&buffer)}); |
|
} |
|
else if(type != other.type) |
|
{ |
|
if(type != -1) |
|
destroy_unsafe(); |
|
type = other.type; |
|
other.visit(copy_visitor{static_cast<void*>(&buffer)}); |
|
} |
|
return *this; |
|
} |
|
variant(variant&& other) |
|
: type(other.type) |
|
{ |
|
if(other.type != -1) |
|
other.visit(move_visitor{static_cast<void*>(&buffer)}); |
|
} |
|
variant& operator=(variant&& other) |
|
{ |
|
if(type == other.type && type != -1) |
|
{ |
|
other.visit(move_assign_visitor{static_cast<void*>(&buffer)}); |
|
} |
|
else if(type != other.type) |
|
{ |
|
if(type != -1) |
|
destroy_unsafe(); |
|
type = other.type; |
|
other.visit(move_visitor{static_cast<void*>(&buffer)}); |
|
} |
|
return *this; |
|
} |
|
~variant() |
|
{ |
|
if(type != -1) |
|
destroy_unsafe(); |
|
} |
|
|
|
void destroy() |
|
{ |
|
if(type != -1) |
|
{ |
|
destroy_unsafe(); |
|
type = -1; |
|
} |
|
} |
|
|
|
void destroy_unsafe() |
|
{ |
|
visit_unsafe(destroy_visitor()); |
|
} |
|
|
|
bool empty() const |
|
{ |
|
return type == -1; |
|
} |
|
|
|
template <typename F> |
|
typename F::result_type visit(F f) const |
|
{ |
|
if(type == -1) |
|
{ |
|
throw std::runtime_error("variant is empty"); |
|
} |
|
else |
|
return call_visitor<0u, sizeof...(Args), std::tuple<Args...>>::call(type, static_cast<const void*>(&buffer), f); |
|
} |
|
|
|
template <typename F> |
|
typename F::result_type visit(F f) |
|
{ |
|
if(type == -1) |
|
{ |
|
throw std::runtime_error("variant is empty"); |
|
} |
|
else |
|
return call_visitor<0u, sizeof...(Args), std::tuple<Args...>>::call(type, static_cast<void*>(&buffer), f); |
|
} |
|
|
|
template <typename F> |
|
typename F::result_type visit_unsafe(F f) const |
|
{ |
|
return call_visitor<0u, sizeof...(Args), std::tuple<Args...>>::call(type, static_cast<const void*>(&buffer), f); |
|
} |
|
|
|
template <typename F> |
|
typename F::result_type visit_unsafe(F f) |
|
{ |
|
return call_visitor<0u, sizeof...(Args), std::tuple<Args...>>::call(type, static_cast<void*>(&buffer), f); |
|
} |
|
|
|
private: |
|
template <typename T> |
|
void construct(T object) |
|
{ |
|
new (&buffer) T(std::move(object)); |
|
} |
|
|
|
typedef typename eina::aligned_union<1, Args...>::type buffer_type; |
|
|
|
friend bool operator==(variant<Args...> const& lhs, variant<Args...> const& rhs) |
|
{ |
|
return rhs.type == lhs.type |
|
&& (rhs.type == -1 |
|
|| rhs.visit(compare_equal_visitor{&lhs.buffer})); |
|
} |
|
|
|
int type; |
|
/** |
|
* Member variable for holding the contained value. |
|
*/ |
|
buffer_type buffer; |
|
}; |
|
|
|
template <typename...Args> |
|
inline bool operator!=(variant<Args...>const& lhs, variant<Args...> const& rhs) |
|
{ |
|
return !(lhs == rhs); |
|
} |
|
|
|
template <typename T, typename...Args> |
|
T* get(variant<Args...>* variant, typename std::enable_if<_impl::is_one_of |
|
<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Args...>::value>::type* = 0) |
|
{ |
|
return variant->visit(get_visitor<T>{}); |
|
} |
|
template <typename T, typename...Args> |
|
T const* get(variant<Args...>const* variant, typename std::enable_if<_impl::is_one_of |
|
<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Args...>::value>::type* = 0) |
|
{ |
|
return variant->visit(get_visitor<T const>{}); |
|
} |
|
template <typename T, typename...Args> |
|
T& get(variant<Args...>& variant, typename std::enable_if<_impl::is_one_of |
|
<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Args...>::value>::type* = 0) |
|
{ |
|
T* p = variant.visit(get_visitor<T>{}); |
|
if(p) |
|
return *p; |
|
else |
|
throw std::logic_error(""); |
|
} |
|
template <typename T, typename...Args> |
|
T const& get(variant<Args...>const& variant, typename std::enable_if<_impl::is_one_of |
|
<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Args...>::value>::type* = 0) |
|
{ |
|
T const* p = variant.visit(get_visitor<T const>{}); |
|
if(p) |
|
return *p; |
|
else |
|
throw std::logic_error(""); |
|
} |
|
|
|
} } |
|
|
|
#endif
|
|
|