You are viewing a plain text version of this content. The canonical link for it is here.
Posted to github@arrow.apache.org by GitBox <gi...@apache.org> on 2020/10/15 18:34:17 UTC

[GitHub] [arrow] pitrou commented on a change in pull request #8472: ARROW-8113: [C++][WIP] Lighter weight variant<>

pitrou commented on a change in pull request #8472:
URL: https://github.com/apache/arrow/pull/8472#discussion_r505758011



##########
File path: cpp/src/arrow/util/variant.h
##########
@@ -17,17 +17,363 @@
 
 #pragma once
 
-#include "arrow/vendored/variant.hpp"  // IWYU pragma: export
+#include <array>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+#include "arrow/util/macros.h"
 
 namespace arrow {
 namespace util {
 
-using ::mpark::bad_variant_access;
-using ::mpark::get;
-using ::mpark::get_if;
-using ::mpark::holds_alternative;
-using ::mpark::variant;
-using ::mpark::visit;
+/// \brief a std::variant-like discriminated union
+///
+/// Simplifications from std::variant:
+///
+/// - Strictly defaultable. The first type of T... must be nothrow default constructible
+///   and it will be used for default Variants.
+///
+/// - Never valueless_by_exception. std::variant supports a state outside those specified
+///   by T... to which it can return in the event that a constructor throws. If a Variant
+///   would become valueless_by_exception it will instead return to its default state.
+///
+/// - Strictly nothrow move constructible and assignable, which is also required of each
+///   of T...
+///
+/// - Less sophisticated type deduction. std::variant<bool, std::string>("hello") will
+///   intelligently construct std::string while Variant will construct bool.
+///
+/// - Either both copy constructible and assignable or neither (std::variant independently
+///   enables copy construction and copy assignment). Variant is copy constructible if
+///   each of T... is copy constructible and assignable.
+///
+/// - Slimmer interface; several members of std::variant are omitted.
+///
+/// - Throws no exceptions; if a bad_variant_access would be thrown Variant will instead
+///   segfault (nullptr dereference).
+///
+/// - Mutable visit takes a pointer instead of mutable reference or rvalue reference,
+///   which is more conformant with our code style.
+template <typename... T>
+class Variant;
+
+namespace detail {
+
+template <typename T, typename = void>
+struct is_equality_comparable : std::false_type {};
+
+template <typename T>
+struct is_equality_comparable<
+    T, typename std::enable_if<std::is_convertible<
+           decltype(std::declval<T>() == std::declval<T>()), bool>::value>::type>
+    : std::true_type {};
+
+template <bool C, typename T, typename E>
+using conditional_t = typename std::conditional<C, T, E>::type;
+
+template <typename T>
+struct type_constant {
+  using type = T;
+};
+
+template <typename...>
+struct first;
+
+template <typename H, typename... T>
+struct first<H, T...> {
+  using type = H;
+};
+
+template <typename T>
+using decay_t = typename std::decay<T>::type;
+
+template <bool...>
+struct all : std::true_type {};
+
+template <bool H, bool... T>
+struct all<H, T...> : conditional_t<H, all<T...>, std::false_type> {};
+
+template <typename T>
+void copy_construct(void* ptr, const void* other) {
+  new (ptr) T(*static_cast<const T*>(other));
+}
+
+template <typename T>
+void move_construct(void* ptr, void* other) {
+  new (ptr) T(std::move(*static_cast<T*>(other)));
+}
+
+template <typename T>
+void explicit_destroy(void* ptr) {
+  static_cast<T*>(ptr)->~T();
+}
+
+inline void trivial_destroy(void*) {}
+
+template <typename T>
+bool equal(const void* l, const void* r) {
+  return *static_cast<const T*>(l) == *static_cast<const T*>(r);
+}
+
+template <typename Visitor, typename T>
+decltype(std::declval<Visitor&&>()(std::declval<const T&>())) visit_const_ref(
+    Visitor&& visitor, const void* ptr) {
+  return std::forward<Visitor>(visitor)(*static_cast<const T*>(ptr));
+}
+
+template <typename Visitor, typename T>
+decltype(std::declval<Visitor&&>()(std::declval<T*>())) visit_mutable_ptr(
+    Visitor&& visitor, void* ptr) {
+  return std::forward<Visitor>(visitor)(static_cast<T*>(ptr));
+}
+
+template <typename... T>
+struct variant_storage {
+  variant_storage() = default;
+  variant_storage(const variant_storage&) {}
+  variant_storage& operator=(const variant_storage&) { return *this; }
+  variant_storage(variant_storage&&) {}
+  variant_storage& operator=(variant_storage&&) { return *this; }
+  ~variant_storage() {
+    static_assert(offsetof(variant_storage, data_) == 0,
+                  "(void*)&variant_storage::data_ == (void*)this");
+  }
+
+  typename std::aligned_union<0, T...>::type data_;
+  uint8_t index_ = 0;
+};
+
+struct delete_copy_constructor {
+  template <typename>
+  struct type {
+    type() = default;
+    type(const type& other) = delete;
+    type& operator=(const type& other) = delete;
+  };
+};
+
+struct explicit_copy_constructor {
+  template <typename Copyable>
+  struct type {
+    type() = default;
+    type(const type& other) {
+      static_cast<Copyable*>(this)->copy(other, /*assignment=*/false);
+    }
+    type& operator=(const type& other) {
+      static_cast<Copyable*>(this)->copy(other, /*assignment=*/true);
+      return *this;
+    }
+  };
+};
+
+template <typename V, uint8_t I, typename...>
+struct member_constructor {
+  static void index_of() {}
+};
+
+template <typename V, uint8_t I, typename H, typename... T>
+struct member_constructor<V, I, H, T...> : member_constructor<V, I + 1, T...> {
+  member_constructor() = default;
+
+  using member_constructor<V, I + 1, T...>::member_constructor;
+  using member_constructor<V, I + 1, T...>::operator=;
+  using member_constructor<V, I + 1, T...>::index_of;
+
+  explicit member_constructor(H value) {
+    new (this) H(std::move(value));
+    static_cast<V*>(this)->index_ = I;
+  }
+
+  member_constructor& operator=(H value) {
+    static_cast<V*>(this)->destroy();
+    new (this) H(std::move(value));
+    static_cast<V*>(this)->index_ = I;
+    return *this;
+  }
+
+  static constexpr std::integral_constant<uint8_t, I> index_of(const type_constant<H>&) {
+    return {};
+  }
+};
+
+}  // namespace detail
+
+template <typename... T>
+class Variant : detail::variant_storage<T...>,
+                detail::conditional_t<
+                    detail::all<(std::is_copy_constructible<T>::value &&
+                                 std::is_copy_assignable<T>::value)...>::value,
+                    detail::explicit_copy_constructor,
+                    detail::delete_copy_constructor>::template type<Variant<T...>>,
+                detail::member_constructor<Variant<T...>, 0, T...> {
+  static_assert(detail::all<(std::is_nothrow_move_constructible<T>::value &&
+                             std::is_nothrow_move_assignable<T>::value)...>::value,
+                "valueless_by_exception is not supported");
+
+  template <typename U>
+  static constexpr uint8_t index_of() {
+    return detail::member_constructor<Variant<T...>, 0, T...>::index_of(
+        detail::type_constant<U>{});
+  }
+
+ public:
+  using default_type = typename util::detail::first<T...>::type;
+  static_assert(std::is_nothrow_default_constructible<default_type>::value,
+                "valueless_by_exception, non-default constructible are not supported");
+
+  Variant() noexcept { construct_default(); }
+
+  Variant(const Variant& other) = default;
+  Variant& operator=(const Variant& other) = default;
+
+  using detail::member_constructor<Variant<T...>, 0, T...>::member_constructor;
+  using detail::member_constructor<Variant<T...>, 0, T...>::operator=;
+
+  Variant(Variant&& other) noexcept { move(&other, /*assignment=*/false); }
+
+  Variant& operator=(Variant&& other) noexcept {
+    move(&other, /*assignment=*/true);
+    return *this;
+  }
+
+  ~Variant() {
+    static_assert(offsetof(Variant, data_) == 0, "(void*)&Variant::data_ == (void*)this");
+    this->destroy();
+  }
+
+  uint8_t index() const noexcept { return this->index_; }
+
+  template <typename U, uint8_t I = index_of<U>()>
+  const U* get() const noexcept {
+    return index() == I ? reinterpret_cast<const U*>(this) : NULLPTR;
+  }
+
+  template <typename U, uint8_t I = index_of<U>()>
+  U* get() noexcept {
+    return index() == I ? reinterpret_cast<U*>(this) : NULLPTR;
+  }
+
+  template <typename U, typename... A, uint8_t I = index_of<U>()>
+  void emplace(A&&... args) try {
+    this->destroy();
+    new (this) U(std::forward<A>(args)...);
+    this->index_ = I;
+  } catch (...) {
+    construct_default();
+    throw;
+  }
+
+  void swap(Variant& other) noexcept {  // NOLINT google-runtime-references
+    Variant tmp = std::move(other);
+    other = std::move(*this);
+    *this = std::move(tmp);
+  }
+
+ private:
+  void move(Variant* other, bool assignment) noexcept {
+    using impl_t = void(void*, void*);
+    static std::array<impl_t*, sizeof...(T)> impl = {detail::move_construct<T>...};

Review comment:
       `constexpr`, no?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org