#ifndef UTIL_VARIANT_HPP #define UTIL_VARIANT_HPP #include #include #include #include // std::move/swap #include // runtime_error #include // operator new #include // size_t #include #ifdef NDEBUG #define VARIANT_INLINE inline __attribute__((always_inline)) #else #define VARIANT_INLINE __attribute__((noinline)) #endif namespace llmr { namespace util { namespace detail { static constexpr std::size_t invalid_value = std::size_t(-1); template struct type_traits; template struct type_traits { static constexpr std::size_t id = std::is_same::value ? sizeof...(Types) : type_traits::id; }; template struct type_traits { static constexpr std::size_t id = invalid_value; }; template struct is_valid_type; template struct is_valid_type { static constexpr bool value = std::is_same::value || is_valid_type::value; }; template struct is_valid_type : std::false_type {}; } template struct static_max; template struct static_max { static const std::size_t value = arg; }; template struct static_max { static const std::size_t value = arg1 >= arg2 ? static_max::value : static_max::value; }; template struct variant_helper; template struct variant_helper { VARIANT_INLINE static void destroy(std::size_t id, void * data) { if (id == sizeof...(Types)) { reinterpret_cast(data)->~T(); } else { variant_helper::destroy(id, data); } } VARIANT_INLINE static void move(const std::size_t old_t, void * old_v, void * new_v) { if (old_t == sizeof...(Types)) { new (new_v) T(std::move(*reinterpret_cast(old_v))); //std::memcpy(new_v, old_v, sizeof(T)); // ^^ DANGER: this should only be considered for relocatable types e.g built-in types // Also, I don't see any measurable performance benefit just yet } else { variant_helper::move(old_t, old_v, new_v); } } VARIANT_INLINE static void copy(const std::size_t old_t, const void * old_v, void * new_v) { if (old_t == sizeof...(Types)) { new (new_v) T(*reinterpret_cast(old_v)); } else { variant_helper::copy(old_t, old_v, new_v); } } }; template<> struct variant_helper<> { VARIANT_INLINE static void destroy(const std::size_t, void *) {} VARIANT_INLINE static void move(const std::size_t, void *, void *) {} VARIANT_INLINE static void copy(const std::size_t, const void *, void *) {} }; namespace detail { template struct dispatcher; template struct dispatcher { typedef typename std::result_of::type result_type; VARIANT_INLINE static result_type apply(V const& v, F f) { if (v.get_type_id() == sizeof...(Types)) { return f(v. template get()); } else { return dispatcher::apply(v, f); } } }; template struct dispatcher { typedef typename std::result_of::type result_type; VARIANT_INLINE static result_type apply(V const&, F) { throw std::runtime_error("dispatch: FAIL"); } }; // comparator functors struct equal_comp { template bool operator()(T const& lhs, T const& rhs) const { return lhs == rhs; } }; struct less_comp { template bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } }; template struct comparer { public: explicit comparer(Variant const& lhs) noexcept : lhs_(lhs) {} comparer& operator=(const comparer&) = delete; // visitor template bool operator()(T const& rhs_content) const { T const& lhs_content = lhs_.template get(); return Comp()(lhs_content, rhs_content); } private: Variant const& lhs_; }; // operator<< helper template struct printer { public: explicit printer(Out & out) : out_(out) {} printer& operator=(const printer&) = delete; // visitor template void operator()(const T& operand) const { out_ << operand; } private: Out & out_; }; } // detail template struct variant { private: static const std::size_t data_size = static_max::value; static const std::size_t data_align = static_max::value; using data_t = typename std::aligned_storage::type; using helper_t = variant_helper; std::size_t type_id; data_t data; public: VARIANT_INLINE variant() : type_id(detail::invalid_value) {} template VARIANT_INLINE explicit variant(T const& val) noexcept : type_id(detail::type_traits::id) { static_assert(detail::is_valid_type::value, "Not a valid type for this variant"); new (&data) T(val); } template VARIANT_INLINE variant(T && val) noexcept : type_id(detail::type_traits::id) { static_assert(detail::is_valid_type::value, "Not a valid type for this variant"); new (&data) T(std::forward(val)); // nothrow } VARIANT_INLINE variant(variant const& old) : type_id(old.type_id) { helper_t::copy(old.type_id, &old.data, &data); } VARIANT_INLINE variant(variant&& old) noexcept : type_id(old.type_id) { helper_t::move(old.type_id, &old.data, &data); } friend void swap(variant & first, variant & second) { using std::swap; //enable ADL swap(first.type_id, second.type_id); swap(first.data, second.data); } VARIANT_INLINE variant& operator= (variant other) { swap(*this, other); return *this; } template VARIANT_INLINE bool is() const { return (type_id == detail::type_traits::id); } VARIANT_INLINE bool valid() const { return (type_id != detail::invalid_value); } template VARIANT_INLINE void set(Args&&... args) { helper_t::destroy(type_id, &data); new (&data) T(std::forward(args)...); type_id = detail::type_traits::id; } template VARIANT_INLINE T& get() { if (type_id == detail::type_traits::id) { return *reinterpret_cast(&data); } else { throw std::runtime_error("in get()"); } } template VARIANT_INLINE T const& get() const { if (type_id == detail::type_traits::id) { return *reinterpret_cast(&data); } else { throw std::runtime_error("in get()"); } } VARIANT_INLINE std::size_t get_type_id() const { return type_id; } // visitor template typename std::result_of::type VARIANT_INLINE static visit(V const& v, F f) { return detail::dispatcher::apply(v, f); } ~variant() noexcept { helper_t::destroy(type_id, &data); } // comparison operators // equality VARIANT_INLINE bool operator==(variant const& rhs) const { if (this->get_type_id() != rhs.get_type_id()) return false; detail::comparer visitor(*this); return visit(rhs, visitor); } // less than VARIANT_INLINE bool operator<(variant const& rhs) const { if (this->get_type_id() != rhs.get_type_id()) { return this->get_type_id() < rhs.get_type_id(); // ^^ borrowed from boost::variant } detail::comparer visitor(*this); return visit(rhs, visitor); } }; // unary visitor interface template typename std::result_of::type VARIANT_INLINE static apply_visitor(V const& v, F f) { return V::visit(v,f); } // operator<< template VARIANT_INLINE std::basic_ostream& operator<< (std::basic_ostream& out, Variant const& rhs) { detail::printer > visitor(out); apply_visitor(rhs, visitor); return out; } } } #endif // UTIL_VARIANT_HPP