// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 // // 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 #include #include namespace td { namespace detail { template class MaxSizeImpl {}; template constexpr const T &constexpr_max(const T &a, const T &b) { return a < b ? b : a; } template class MaxSizeImpl { public: static constexpr size_t value = MaxSizeImpl::value; }; template class MaxSizeImpl { public: static constexpr size_t value = Res; }; template class MaxSize { public: static constexpr size_t value = MaxSizeImpl<0, sizeof(Args)...>::value; }; template class IthTypeImpl {}; template class IthTypeImpl<0, Res, Args...> { public: using type = Res; }; template class IthTypeImpl : public IthTypeImpl {}; class Dummy {}; template class IthType : public IthTypeImpl {}; template class FindTypeOffsetImpl {}; template class FindTypeOffsetImpl { public: static constexpr int value = offset; }; template class FindTypeOffsetImpl : public FindTypeOffsetImpl::value, offset + 1, T, Types...> {}; template class FindTypeOffset : public FindTypeOffsetImpl {}; template class ForEachTypeImpl {}; template class ForEachTypeImpl { public: template static void visit(F &&f) { } }; template class ForEachTypeImpl { public: template static void visit(F &&f) { f(offset, static_cast(nullptr)); ForEachTypeImpl::visit(f); } }; template class ForEachType { public: template static void visit(F &&f) { ForEachTypeImpl<0, Types..., Dummy>::visit(f); } }; } // namespace detail template class Variant { public: static constexpr int npos = -1; Variant() { } Variant(Variant &&other) { other.visit([&](auto &&value) { this->init_empty(std::forward(value)); }); } Variant(const Variant &other) { other.visit([&](auto &&value) { this->init_empty(std::forward(value)); }); } Variant &operator=(Variant &&other) { clear(); other.visit([&](auto &&value) { this->init_empty(std::forward(value)); }); return *this; } Variant &operator=(const Variant &other) { clear(); other.visit([&](auto &&value) { this->init_empty(std::forward(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; if (offset == offset_) { res = this->get() == other.template get(); } }); 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; if (offset == offset_) { res = this->get() < other.template get(); } }); return res; } template Variant(T &&t) { init_empty(std::forward(t)); } template Variant &operator=(T &&t) { clear(); init_empty(std::forward(t)); return *this; } template static constexpr int offset() { return detail::FindTypeOffset, Types...>::value; } template void init_empty(T &&t) { CHECK(offset_ == npos) << offset_ #if TD_CLANG || TD_GCC << __PRETTY_FUNCTION__ #endif ; offset_ = offset(); new (&get()) std::decay_t(std::forward(t)); } ~Variant() { clear(); } template void visit(F &&f) { for_each([&](int offset, auto *ptr) { using T = std::decay_t; if (offset == offset_) { f(std::move(*this->get_unsafe())); } }); } template void for_each(F &&f) { detail::ForEachType::visit(f); } template void visit(F &&f) const { for_each([&](int offset, auto *ptr) { using T = std::decay_t; if (offset == offset_) { f(std::move(*this->get_unsafe())); } }); } template void for_each(F &&f) const { detail::ForEachType::visit(f); } void clear() { visit([](auto &&value) { using T = std::decay_t; value.~T(); }); offset_ = npos; } template auto &get() { CHECK(offset == offset_); return *get_unsafe(); } template auto &get() { return get()>(); } template const auto &get() const { CHECK(offset == offset_); return *get_unsafe(); } template const auto &get() const { return get()>(); } int32 get_offset() const { return offset_; } private: union { int64 align_; char data_[detail::MaxSize::value]; }; int offset_{npos}; template auto *get_unsafe() { return reinterpret_cast(data_); } template auto *get_unsafe() { using T = typename detail::IthType::type; return get_unsafe(); } template const auto *get_unsafe() const { return reinterpret_cast(data_); } template const auto *get_unsafe() const { using T = typename detail::IthType::type; return get_unsafe(); } }; template auto &get(Variant &v) { return v.template get(); } template auto &get(const Variant &v) { return v.template get(); } template auto &get(Variant &v) { return v.template get(); } template auto &get(const Variant &v) { return v.template get(); } } // namespace td