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



##########
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>...};
+
+    if (assignment) {
+      this->destroy();
+    }
+
+    auto index = other->index_;
+    impl[index](this, other);

Review comment:
       Clang 10 succeeds: https://godbolt.org/z/ab8Gaf
   
   Moreover, function tables are also the approach taken by 
vendored/variant.hpp for c++11 when last I checked




----------------------------------------------------------------
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:
[email protected]


Reply via email to