Created
April 25, 2019 12:10
-
-
Save pnck/08ee0137f7d78f7644ff5b359339df47 to your computer and use it in GitHub Desktop.
simple variant implement for c++11/14
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef UTIL_VARIANT_HPP | |
#define UTIL_VARIANT_HPP | |
#include <type_traits> | |
#include <memory> | |
#include <tuple> | |
#if __cplusplus >= 201703L | |
#include <variant> | |
namespace util { | |
template <class... Ts> | |
using variant = std::variant<Ts...>; | |
} | |
#else | |
namespace util { | |
template <typename OP> | |
struct fold_helper { | |
template <typename T> | |
static constexpr bool fold(const T &vs) { | |
return bool(vs); | |
} | |
template <typename T1, typename ...Ts> | |
static constexpr bool fold(const T1 &v1, const Ts &...vs) { | |
return OP{}(bool(v1), fold(vs...)); | |
} | |
}; | |
template <typename ...Ts> | |
inline constexpr bool fold_or(Ts &&...v) { | |
return fold_helper<std::logical_or<>>::fold(std::forward<Ts>(v)...); | |
} | |
template <typename ...Ts> | |
inline constexpr bool fold_and(Ts &&...v) { | |
return fold_helper<std::logical_and<>>::fold(std::forward<Ts>(v)...); | |
} | |
template <typename, typename ...> | |
struct index_of : std::integral_constant<size_t, 0> {}; | |
template <typename T, typename T1, typename ...Ts> | |
struct index_of<T, T1, Ts...> : | |
std::integral_constant<size_t, std::is_same<T, T1>::value ? | |
0 : | |
index_of<T, Ts...>::value + 1> { | |
}; | |
template <typename T, typename ...Ts> | |
constexpr size_t index_of_v = index_of<T, Ts...>::value; | |
template <typename ...Ts> | |
struct variant_type { | |
private: | |
static_assert(sizeof...(Ts) > 0, | |
"variant must have at least one alternative"); | |
static_assert(!fold_or(::std::is_reference<Ts>::value ...), | |
"variant must have no reference alternative"); | |
static_assert(!fold_or(std::is_void<Ts>::value...), | |
"variant must have no void alternative"); | |
std::tuple<std::unique_ptr<Ts>...> values_; | |
size_t activating_ = 0; | |
public: | |
template <size_t I> | |
using type_at = typename std::remove_reference_t<decltype(std::get<I>(values_))>::element_type; | |
public: // container types | |
template <typename T, | |
typename = std::enable_if_t<!std::is_reference<T>::value>, | |
typename = std::enable_if_t<index_of_v<T, Ts...> < sizeof...(Ts)> | |
> | |
explicit variant_type(const T &t) { // copy | |
constexpr auto activating = index_of_v<T, Ts...>; | |
std::get<activating>(values_) = std::make_unique<T>(t); | |
activating_ = activating; | |
} | |
template <typename T, | |
typename = std::enable_if_t<!std::is_reference<T>::value>, | |
typename = std::enable_if_t<index_of_v<T, Ts...> < sizeof...(Ts)> | |
> | |
explicit variant_type(T &&t) {// move | |
constexpr auto activating = index_of_v<T, Ts...>; | |
std::get<activating>(values_) = std::make_unique<T>(std::move(t)); | |
activating_ = activating; | |
} | |
template <typename T, | |
typename = std::enable_if_t<!std::is_reference<T>::value>, | |
typename = std::enable_if_t<index_of_v<T, Ts...> < sizeof...(Ts)> | |
> | |
variant_type &operator=(const T &t) {// copy | |
constexpr auto activating = index_of_v<T, Ts...>; | |
std::get<activating>(values_) = std::make_unique<T>(t); | |
activating_ = activating; | |
return *this; | |
} | |
template <typename T, | |
typename = std::enable_if_t<!std::is_reference<T>::value>, | |
typename = std::enable_if_t<index_of_v<T, Ts...> < sizeof...(Ts)> | |
> | |
variant_type &operator=(T &&t) {// move | |
constexpr auto activating = index_of_v<T, Ts...>; | |
std::get<activating>(values_) = std::make_unique<T>(std::move(t)); | |
activating_ = activating; | |
return *this; | |
} | |
template <typename T> | |
T &get() { // non const | |
if (activating_ == index_of_v<T, Ts...>) { | |
return *std::get<index_of_v<T, Ts...>>(values_); | |
} else { | |
throw std::invalid_argument("Unexpected type"); | |
} | |
} | |
template <typename T> | |
const T &get() const { // const | |
if (activating_ == index_of_v<T, Ts...>) { | |
return *std::get<index_of_v<T, Ts...>>(values_); | |
} else { | |
throw std::invalid_argument("Unexpected type"); | |
} | |
} | |
template <size_t I> | |
type_at<I> &get() { // non const | |
if (activating_ == I) { | |
return *std::get<I>(values_); | |
} else { | |
throw std::invalid_argument("Unexpected type"); | |
} | |
} | |
template <size_t I> | |
const type_at<I> &get() const { // const | |
if (activating_ == I) { | |
return *std::get<I>(values_); | |
} else { | |
throw std::invalid_argument("Unexpected type"); | |
} | |
} | |
private: | |
template <size_t I> | |
inline int copy_helper_impl(const variant_type<Ts...> &v) { | |
// make unique_ptr with object copy constructor | |
for (size_t i = 0; i <= I; ++i) { | |
auto &p = std::get<I>(v.values_); | |
if (p) { | |
std::get<I>(values_) = std::make_unique<type_at<I>>(*p); | |
} | |
} | |
return I; | |
} | |
template <size_t ...Is> | |
inline void copy_helper(const variant_type<Ts...> &v, std::index_sequence<Is...>) { | |
auto _ = {copy_helper_impl<Is>(v)...}; | |
} | |
public: // variant self type | |
variant_type() = default; | |
~variant_type() = default; | |
variant_type(const variant_type<Ts...> &v) { // copy | |
activating_ = v.activating_; | |
copy_helper(v, std::make_index_sequence<sizeof...(Ts)>{}); | |
} | |
variant_type(variant_type<Ts...> &&v) noexcept { // move | |
activating_ = v.activating_; | |
values_.swap(v.values_); | |
v.activating_ = sizeof...(Ts) + 1; // invalidate | |
} | |
variant_type &operator=(const variant_type<Ts...> &v) { // copy | |
activating_ = v.activating_; | |
copy_helper(v, std::make_index_sequence<sizeof...(Ts)>{}); | |
return *this; | |
} | |
variant_type &operator=(variant_type<Ts...> &&v) noexcept { // move | |
activating_ = v.activating_; | |
values_ = std::move(v.values_); | |
v.activating_ = sizeof...(Ts) + 1; // invalidate | |
return *this; | |
} | |
}; | |
#endif // if __cplusplus >= 201703L | |
} | |
#if __cplusplus < 201703L | |
namespace std { // NOLINT(cert-dcl58-cpp) | |
template <typename T, typename ...Ts> | |
T &get(util::variant_type<Ts...> &v) { | |
return v.template get<T>(); | |
} | |
template <size_t I, typename ...Ts> | |
typename util::variant_type<Ts...>::template type_at<I> &get(util::variant_type<Ts...> &v) { | |
return v.template get<I>(); | |
} | |
template <typename T, typename ...Ts> | |
const T &get(const util::variant_type<Ts...> &v) { | |
return v.template get<T>(); | |
} | |
template <size_t I, typename ...Ts> | |
const typename util::variant_type<Ts...>::template type_at<I> &get(const util::variant_type<Ts...> &v) { | |
return v.template get<I>(); | |
} | |
} | |
#endif | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment