浏览代码

Move variants into their own namespace. Pull helpers into the detail namespace.

Sam Jaffe 5 年之前
父节点
当前提交
aae792dba7
共有 5 个文件被更改,包括 198 次插入170 次删除
  1. 37 0
      include/variant/detail/helper.hpp
  2. 33 0
      include/variant/detail/types.hpp
  3. 109 154
      include/variant/variant.hpp
  4. 17 15
      test/gtest_variant_printers.h
  5. 2 1
      test/variant_test.cxx

+ 37 - 0
include/variant/detail/helper.hpp

@@ -0,0 +1,37 @@
+#pragma once
+
+namespace variant { namespace detail {
+  template <typename... Ts> struct helper;
+
+  template <typename F, typename... Ts> struct helper<F, Ts...> {
+    inline static void destroy(size_t id, void * data) {
+      if (id == sizeof...(Ts)) {
+        reinterpret_cast<F *>(data)->~F();
+      } else {
+        helper<Ts...>::destroy(id, data);
+      }
+    }
+
+    inline static void move(size_t old_t, void * old_v, void * new_v) {
+      if (old_t == sizeof...(Ts)) {
+        new (new_v) F(std::move(*reinterpret_cast<F *>(old_v)));
+      } else {
+        helper<Ts...>::move(old_t, old_v, new_v);
+      }
+    }
+
+    inline static void copy(size_t old_t, const void * old_v, void * new_v) {
+      if (old_t == sizeof...(Ts)) {
+        new (new_v) F(*reinterpret_cast<const F *>(old_v));
+      } else {
+        helper<Ts...>::copy(old_t, old_v, new_v);
+      }
+    }
+  };
+
+  template <> struct helper<> {
+    inline static void destroy(size_t, void *) {}
+    inline static void move(size_t, void *, void *) {}
+    inline static void copy(size_t, const void *, void *) {}
+  };
+}}

+ 33 - 0
include/variant/detail/types.hpp

@@ -0,0 +1,33 @@
+#pragma once
+
+namespace variant { namespace detail {
+
+  // Max of a list of values
+  template <size_t T, size_t... Ts> struct static_max;
+
+  template <size_t T> struct static_max<T> {
+    static const constexpr size_t value = T;
+  };
+
+  template <size_t T1, size_t T2, size_t... Ts>
+  struct static_max<T1, T2, Ts...> {
+    static const constexpr size_t value =
+        T1 > T2 ? static_max<T1, Ts...>::value : static_max<T2, Ts...>::value;
+  };
+
+  // Given a typelist of size N, type_index<F>::value will return the inverted
+  // offset (i.e. the number of elements that remain after F in the typelist).
+  template <typename F, typename... Ts> struct type_index;
+
+  template <typename F> struct type_index<F> {};
+
+  template <typename F, typename T, typename... Ts>
+  struct type_index<F, T, Ts...> {
+    static const constexpr size_t value = type_index<F, Ts...>::value;
+  };
+
+  template <typename F, typename... Ts> struct type_index<F, F, Ts...> {
+    static const constexpr size_t value = sizeof...(Ts);
+  };
+
+}}

+ 109 - 154
include/variant/variant.hpp

@@ -6,174 +6,129 @@
 //  Copyright © 2016 Sam Jaffe. All rights reserved.
 //
 
-#ifndef variant_h
-#define variant_h
 #pragma once
 
 #include <cstdlib>
 #include <memory>
 #include <utility>
 
-// Max of a list of values
-template <size_t T, size_t... Ts> struct static_max;
+#include "detail/helper.hpp"
+#include "detail/types.hpp"
+
+namespace variant {
+  template <typename... Ts> class variant {
+  public:
+    template <size_t I>
+    using at = typename std::tuple_element<I, std::tuple<Ts...>>::type;
+
+  private:
+    static const constexpr size_t num_types = sizeof...(Ts);
+    static const constexpr size_t data_size =
+        detail::static_max<sizeof(Ts)...>::value;
+    static const constexpr size_t data_align =
+        detail::static_max<alignof(Ts)...>::value;
+
+    using data_t = typename std::aligned_storage<data_size, data_align>::type;
+
+  private:
+    size_t type_id;
+    data_t data;
+
+  public:
+    variant() : type_id(invalid_type()), data() {}
+
+    template <typename T>
+    variant(T const & value) : type_id(invalid_type()), data() {
+      set<T>(value);
+    }
+
+    variant(const variant<Ts...> & old) : type_id(old.type_id), data() {
+      detail::helper<Ts...>::copy(num_types - type_id - 1, &old.data, &data);
+    }
+
+    variant(variant && old) : type_id(old.type_id), data() {
+      detail::helper<Ts...>::move(num_types - type_id - 1, &old.data, &data);
+      old.type_id = invalid_type();
+    }
+
+    variant & operator=(variant const & old) {
+      detail::helper<Ts...>::destroy(num_types - type_id - 1, &data);
+      type_id = old.type_id;
+      detail::helper<Ts...>::copy(num_types - type_id - 1, &old.data, &data);
+      return *this;
+    }
+
+    variant & operator=(variant && old) {
+      detail::helper<Ts...>::destroy(num_types - type_id - 1, &data);
+      type_id = old.type_id;
+      detail::helper<Ts...>::move(num_types - type_id - 1, &old.data, &data);
+      old.type_id = invalid_type();
+      return *this;
+    }
+
+    ~variant() {
+      detail::helper<Ts...>::destroy(num_types - type_id - 1, &data);
+    }
+
+    template <typename T> inline bool is() const {
+      return (type_id == get_type_index<T>());
+    }
+
+    inline bool valid() const { return (type_id != invalid_type()); }
+
+    template <typename T, typename... Args> void set(Args &&... args) {
+      // First we destroy the current contents
+      detail::helper<Ts...>::destroy(num_types - type_id - 1, &data);
+      new (&data) T(std::forward<Args>(args)...);
+      type_id = get_type_index<T>();
+    }
+
+    template <typename T> T & get() {
+      // It is a dynamic_cast-like behaviour
+      if (is<T>()) {
+        return *reinterpret_cast<T *>(&data);
+      } else {
+        throw std::bad_cast();
+      }
+    }
+
+    template <typename T> T const & get() const {
+      // It is a dynamic_cast-like behaviour
+      if (is<T>()) {
+        return *reinterpret_cast<T const *>(&data);
+      } else {
+        throw std::bad_cast();
+      }
+    }
+
+    size_t index() const { return type_id; }
+
+    template <size_t I> auto get() const -> at<I> const & {
+      return get<at<I>>();
+    }
+
+    template <size_t I> auto get() -> at<I> & { return get<at<I>>(); }
+
+  private:
+    static inline size_t invalid_type() { return 0xFFFFFFFF; }
+
+    template <typename T> static inline size_t get_type_index() {
+      return num_types - detail::type_index<T, Ts...>::value - 1;
+    }
+  };
 
-template <size_t T> struct static_max<T> {
-  static const constexpr size_t value = T;
-};
-
-template <size_t T1, size_t T2, size_t... Ts> struct static_max<T1, T2, Ts...> {
-  static const constexpr size_t value =
-      T1 > T2 ? static_max<T1, Ts...>::value : static_max<T2, Ts...>::value;
-};
-
-// Type index in a list
-template <typename F, typename... Ts> struct type_index;
-
-template <typename F> struct type_index<F> {};
-
-template <typename F, typename T, typename... Ts>
-struct type_index<F, T, Ts...> {
-  static const constexpr size_t value = type_index<F, Ts...>::value;
-};
-
-template <typename F, typename... Ts> struct type_index<F, F, Ts...> {
-  static const constexpr size_t value = sizeof...(Ts);
-};
-
-template <typename... Ts> struct variant_helper;
-
-template <typename F, typename... Ts> struct variant_helper<F, Ts...> {
-  inline static void destroy(size_t id, void * data) {
-    if (id == sizeof...(Ts))
-      reinterpret_cast<F *>(data)->~F();
-    else
-      variant_helper<Ts...>::destroy(id, data);
-  }
-
-  inline static void move(size_t old_t, void * old_v, void * new_v) {
-    if (old_t == sizeof...(Ts))
-      new (new_v) F(std::move(*reinterpret_cast<F *>(old_v)));
-    else
-      variant_helper<Ts...>::move(old_t, old_v, new_v);
-  }
-
-  inline static void copy(size_t old_t, const void * old_v, void * new_v) {
-    if (old_t == sizeof...(Ts))
-      new (new_v) F(*reinterpret_cast<const F *>(old_v));
-    else
-      variant_helper<Ts...>::copy(old_t, old_v, new_v);
-  }
-};
-
-template <> struct variant_helper<> {
-  inline static void destroy(size_t, void *) {}
-  inline static void move(size_t, void *, void *) {}
-  inline static void copy(size_t, const void *, void *) {}
-};
-
-template <typename... Ts> struct variant {
-private:
-  static const constexpr size_t num_types = sizeof...(Ts);
-  static const constexpr size_t data_size = static_max<sizeof(Ts)...>::value;
-  static const constexpr size_t data_align = static_max<alignof(Ts)...>::value;
-
-  using data_t = typename std::aligned_storage<data_size, data_align>::type;
-
-  using helper_t = variant_helper<Ts...>;
-
-  static inline size_t invalid_type() { return 0xFFFFFFFF; }
-
-  template <typename T> static inline size_t get_type_index() {
-    return num_types - type_index<T, Ts...>::value - 1;
-  }
-
-  size_t type_id;
-  data_t data;
-
-public:
-  template <size_t I>
-  using at = typename std::tuple_element<I, std::tuple<Ts...>>::type;
-
-  variant() : type_id(invalid_type()), data() {}
-
-  template <typename T>
-  variant(T const & value) : type_id(invalid_type()), data() {
-    set<T>(value);
-  }
-
-  variant(const variant<Ts...> & old) : type_id(old.type_id), data() {
-    helper_t::copy(num_types - type_id - 1, &old.data, &data);
-  }
-
-  variant(variant && old) : type_id(old.type_id), data() {
-    helper_t::move(num_types - type_id - 1, &old.data, &data);
-    old.type_id = invalid_type();
-  }
-
-  variant & operator=(variant const & old) {
-    helper_t::destroy(num_types - type_id - 1, &data);
-    type_id = old.type_id;
-    helper_t::copy(num_types - type_id - 1, &old.data, &data);
-    return *this;
-  }
-
-  variant & operator=(variant && old) {
-    helper_t::destroy(num_types - type_id - 1, &data);
-    type_id = old.type_id;
-    helper_t::move(num_types - type_id - 1, &old.data, &data);
-    old.type_id = invalid_type();
-    return *this;
-  }
-
-  template <typename T> inline bool is() const {
-    return (type_id == get_type_index<T>());
-  }
-
-  inline bool valid() const { return (type_id != invalid_type()); }
-
-  template <typename T, typename... Args> void set(Args &&... args) {
-    // First we destroy the current contents
-    helper_t::destroy(num_types - type_id - 1, &data);
-    new (&data) T(std::forward<Args>(args)...);
-    type_id = get_type_index<T>();
-  }
-
-  template <typename T> T & get() {
-    // It is a dynamic_cast-like behaviour
-    if (is<T>())
-      return *reinterpret_cast<T *>(&data);
-    else
-      throw std::bad_cast();
-  }
-
-  template <typename T> T const & get() const {
-    // It is a dynamic_cast-like behaviour
-    if (is<T>())
-      return *reinterpret_cast<T const *>(&data);
-    else
-      throw std::bad_cast();
-  }
-
-  size_t index() const { return type_id; }
-
-  template <size_t I> auto get() const -> at<I> const & { return get<at<I>>(); }
-
-  template <size_t I> auto get() -> at<I> & { return get<at<I>>(); }
-
-  ~variant() { helper_t::destroy(num_types - type_id - 1, &data); }
-};
+}
 
 namespace std {
   template <size_t I, typename... Ts>
-  auto get(variant<Ts...> const & var) ->
-      typename variant<Ts...>::template at<I> const & {
+  auto get(variant::variant<Ts...> const & var) ->
+      typename variant::variant<Ts...>::template at<I> const & {
     return var.template get<I>();
   }
 
   template <size_t I, typename... Ts>
-  auto get(variant<Ts...> & var) -> typename variant<Ts...>::template at<I> & {
+  auto get(variant::variant<Ts...> & var) ->
+      typename variant::variant<Ts...>::template at<I> & {
     return var.template get<I>();
   }
 }
-
-#endif /* variant_h */

+ 17 - 15
test/gtest_variant_printers.h

@@ -21,22 +21,24 @@ std::ostream & operator<<(std::ostream & os, std::map<K, V> const & value) {
   return os << " }";
 }
 
-template <std::size_t I, typename... Ts>
-void PrintTo(variant<Ts...> const & value, std::ostream * os) {
-  if (value.index() == I) { (*os) << value.template get<I>(); }
-}
+namespace variant {
+  template <std::size_t I, typename... Ts>
+  void PrintTo(variant<Ts...> const & value, std::ostream * os) {
+    if (value.index() == I) { (*os) << value.template get<I>(); }
+  }
 
-template <typename... Ts, std::size_t... Is>
-void PrintTo(variant<Ts...> const & value, std::ostream * os,
-             std::index_sequence<Is...>) {
-  [[maybe_unused]] auto l = {(PrintTo<Is>(value, os), 0)...};
-}
+  template <typename... Ts, std::size_t... Is>
+  void PrintTo(variant<Ts...> const & value, std::ostream * os,
+               std::index_sequence<Is...>) {
+    [[maybe_unused]] auto l = {(PrintTo<Is>(value, os), 0)...};
+  }
 
-template <typename... Ts>
-void PrintTo(variant<Ts...> const & value, std::ostream * os) {
-  if (value.valid()) {
-    PrintTo(value, os, std::make_index_sequence<sizeof...(Ts)>());
-  } else {
-    (*os) << "<invalid>";
+  template <typename... Ts>
+  void PrintTo(variant<Ts...> const & value, std::ostream * os) {
+    if (value.valid()) {
+      PrintTo(value, os, std::make_index_sequence<sizeof...(Ts)>());
+    } else {
+      (*os) << "<invalid>";
+    }
   }
 }

+ 2 - 1
test/variant_test.cxx

@@ -17,7 +17,8 @@
 #include "gtest_variant_matchers.h"
 #include "gtest_variant_printers.h"
 
-using test_variant = variant<int, bool, std::string, std::map<int, int>>;
+using test_variant =
+    variant::variant<int, bool, std::string, std::map<int, int>>;
 
 TEST(VariantTest, DefaultVariantIsTypeless) {
   test_variant test;