// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once #include "td/utils/common.h" #include "td/utils/logging.h" #include <new> #include <type_traits> #include <utility> namespace td { namespace detail { template <size_t... Args> class MaxSizeImpl {}; template <class T> constexpr const T &constexpr_max(const T &a, const T &b) { return a < b ? b : a; } template <size_t Res, size_t X, size_t... Args> class MaxSizeImpl<Res, X, Args...> { public: static constexpr size_t value = MaxSizeImpl<constexpr_max(Res, X), Args...>::value; }; template <size_t Res> class MaxSizeImpl<Res> { public: static constexpr size_t value = Res; }; template <class... Args> class MaxSize { public: static constexpr size_t value = MaxSizeImpl<0, sizeof(Args)...>::value; }; template <size_t to_skip, class... Args> class IthTypeImpl {}; template <class Res, class... Args> class IthTypeImpl<0, Res, Args...> { public: using type = Res; }; template <size_t pos, class Skip, class... Args> class IthTypeImpl<pos, Skip, Args...> : public IthTypeImpl<pos - 1, Args...> {}; class Dummy {}; template <size_t pos, class... Args> class IthType : public IthTypeImpl<pos, Args..., Dummy> {}; template <bool ok, int offset, class... Types> class FindTypeOffsetImpl {}; template <int offset, class... Types> class FindTypeOffsetImpl<true, offset, Types...> { public: static constexpr int value = offset; }; template <int offset, class T, class S, class... Types> class FindTypeOffsetImpl<false, offset, T, S, Types...> : public FindTypeOffsetImpl<std::is_same<T, S>::value, offset + 1, T, Types...> {}; template <class T, class... Types> class FindTypeOffset : public FindTypeOffsetImpl<false, -1, T, Types...> {}; template <int offset, class... Types> class ForEachTypeImpl {}; template <int offset> class ForEachTypeImpl<offset, Dummy> { public: template <class F> static void visit(F &&f) { } }; template <int offset, class T, class... Types> class ForEachTypeImpl<offset, T, Types...> { public: template <class F> static void visit(F &&f) { f(offset, static_cast<T *>(nullptr)); ForEachTypeImpl<offset + 1, Types...>::visit(f); } }; template <class... Types> class ForEachType { public: template <class F> static void visit(F &&f) { ForEachTypeImpl<0, Types..., Dummy>::visit(f); } }; } // namespace detail template <class... Types> class Variant { public: static constexpr int npos = -1; Variant() { } Variant(Variant &&other) noexcept { other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); }); } Variant(const Variant &other) { other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); }); } Variant &operator=(Variant &&other) { clear(); other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); }); return *this; } Variant &operator=(const Variant &other) { clear(); other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); }); return *this; } bool operator==(const Variant &other) const { if (offset_ != other.offset_) { return false; } bool res = false; for_each([&](int offset, auto *ptr) { using T = std::decay_t<decltype(*ptr)>; if (offset == offset_) { res = this->get<T>() == other.template get<T>(); } }); return res; } bool operator<(const Variant &other) const { if (offset_ != other.offset_) { return offset_ < other.offset_; } bool res = false; for_each([&](int offset, auto *ptr) { using T = std::decay_t<decltype(*ptr)>; if (offset == offset_) { res = this->get<T>() < other.template get<T>(); } }); return res; } template <class T, std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value, int> = 0> Variant(T &&t) { init_empty(std::forward<T>(t)); } template <class T, std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value, int> = 0> Variant &operator=(T &&t) { clear(); init_empty(std::forward<T>(t)); return *this; } template <class T> static constexpr int offset() { return detail::FindTypeOffset<std::decay_t<T>, Types...>::value; } template <class T> void init_empty(T &&t) { LOG_CHECK(offset_ == npos) << offset_ #if TD_CLANG || TD_GCC << ' ' << __PRETTY_FUNCTION__ #endif ; offset_ = offset<T>(); new (&get<T>()) std::decay_t<T>(std::forward<T>(t)); } ~Variant() { clear(); } template <class F> void visit(F &&f) { for_each([&](int offset, auto *ptr) { using T = std::decay_t<decltype(*ptr)>; if (offset == offset_) { f(std::move(*this->get_unsafe<T>())); } }); } template <class F> void for_each(F &&f) { detail::ForEachType<Types...>::visit(f); } template <class F> void visit(F &&f) const { for_each([&](int offset, auto *ptr) { using T = std::decay_t<decltype(*ptr)>; if (offset == offset_) { f(std::move(*this->get_unsafe<T>())); } }); } template <class F> void for_each(F &&f) const { detail::ForEachType<Types...>::visit(f); } void clear() { visit([](auto &&value) { using T = std::decay_t<decltype(value)>; value.~T(); }); offset_ = npos; } template <int offset> auto &get() { CHECK(offset == offset_); return *get_unsafe<offset>(); } template <class T> auto &get() { return get<offset<T>()>(); } template <int offset> const auto &get() const { CHECK(offset == offset_); return *get_unsafe<offset>(); } template <class T> const auto &get() const { return get<offset<T>()>(); } int32 get_offset() const { return offset_; } private: union { int64 align_; char data_[detail::MaxSize<Types...>::value]; }; int offset_{npos}; template <class T> auto *get_unsafe() { return reinterpret_cast<T *>(data_); } template <int offset> auto *get_unsafe() { using T = typename detail::IthType<offset, Types...>::type; return get_unsafe<T>(); } template <class T> const auto *get_unsafe() const { return reinterpret_cast<const T *>(data_); } template <int offset> const auto *get_unsafe() const { using T = typename detail::IthType<offset, Types...>::type; return get_unsafe<T>(); } }; template <class T, class... Types> auto &get(Variant<Types...> &v) { return v.template get<T>(); } template <class T, class... Types> auto &get(const Variant<Types...> &v) { return v.template get<T>(); } template <int T, class... Types> auto &get(Variant<Types...> &v) { return v.template get<T>(); } template <int T, class... Types> auto &get(const Variant<Types...> &v) { return v.template get<T>(); } } // namespace td