mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			303 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			303 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|     This file is part of TON Blockchain Library.
 | |
| 
 | |
|     TON Blockchain Library is free software: you can redistribute it and/or modify
 | |
|     it under the terms of the GNU Lesser General Public License as published by
 | |
|     the Free Software Foundation, either version 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     TON Blockchain Library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public License
 | |
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
|     Copyright 2017-2019 Telegram Systems LLP
 | |
| */
 | |
| #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
 |