Skip to content

Instantly share code, notes, and snippets.

@pnck
Created April 25, 2019 12:10
Show Gist options
  • Save pnck/08ee0137f7d78f7644ff5b359339df47 to your computer and use it in GitHub Desktop.
Save pnck/08ee0137f7d78f7644ff5b359339df47 to your computer and use it in GitHub Desktop.
simple variant implement for c++11/14
#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