You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@iceberg.apache.org by "zhjwpku (via GitHub)" <gi...@apache.org> on 2025/01/26 16:50:05 UTC

[PR] backport c++23 std::expected [iceberg-cpp]

zhjwpku opened a new pull request, #40:
URL: https://github.com/apache/iceberg-cpp/pull/40

   After studied [0], [1] and [2], I'm inclined to backport [0] to iceberg-cpp, the main reason is that [0] has exactly the same APIs as std::expected, [1] provide some extra member functions like `map` and `map_error`, [2] has different API names like `then` and `thenOrThrow`.
   
   We want users to use iceberg::expected the same way as std::expected, and we will replace iceberg::expected with std::expected when we decide to move to c++23 some day, by backporting [0], we can facilitate a smoother transition process.
   
   We discussed about `Exceptions vs Expected` in #14, while backporting [0], I had the feeling we shouldn't choose one over the other, we can use both approaches effectively.
   
   expected provide monadic operations like `and_then`, `transform`, `or_else`, `transform_error`, let us do method chaining, which is a good sign.
   
   [0] https://github.com/zeus-cpp/expected
   [1] https://github.com/TartanLlama/expected
   [2] https://github.com/facebook/folly/blob/main/folly/Expected.h
   [3] https://en.cppreference.com/w/cpp/utility/expected


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2633426269

   > LGTM. IMO let's get moving on this.
   > 
   > BTW, we could consider writing a custom check to just enforce `[[nodiscard]]` on functions returning `expected`? https://clang.llvm.org/extra/clang-tidy/Contributing.html
   
   Reasonable, will file an issue once this PR got merged.


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "wgtmac (via GitHub)" <gi...@apache.org>.
wgtmac commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2635820910

   Is it possible to check the macro [__cpp_lib_expected](https://en.cppreference.com/w/cpp/feature_test#cpp_lib_expected) and directly use `std::expected` if available?


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "pitrou (via GitHub)" <gi...@apache.org>.
pitrou commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2636748429

   > Is it possible to check the macro [__cpp_lib_expected](https://en.cppreference.com/w/cpp/feature_test#cpp_lib_expected) and directly use `std::expected` if available?
   
   That may lead to ABI issues. For example if iceberg-cpp is compiled using a C++20 compiler, and then an application links to it with C++23 enabled. Or the reverse.


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "wgtmac (via GitHub)" <gi...@apache.org>.
wgtmac commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2636892096

   > That may lead to ABI issues. For example if iceberg-cpp is compiled using a C++20 compiler, and then an application links to it with C++23 enabled. Or the reverse.
   
   Make sense. Thanks!


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930259835


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   I think we should still keep the MIT license



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930185280


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   I'm not sure I understand this, IMO adding [[nodiscard]] to iceberg::expected will force users to handle the returned iceberg::expected, switch to std::expected will loosen this restriction. Please correct me if I'm wrong ;)



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930406377


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   I add the zeus-cpp to our LICENSE file, for the header I think ASF 2 license is fine, I checked the following:
   
   - https://github.com/apache/arrow/blob/main/LICENSE.txt#L1991-L2016
   - https://github.com/apache/arrow/blob/main/LICENSE.txt#L482-L503
   - https://github.com/apache/arrow/blob/main/LICENSE.txt#L559-L581
   
   they all have ASF 2 license header
   
   - https://github.com/apache/arrow/blob/main/cpp/cmake_modules/BuildUtils.cmake
   - https://github.com/apache/arrow/blob/main/r/configure
   - https://github.com/apache/arrow/blob/main/cpp/src/arrow/util/utf8.h
   



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1939415372


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   I believe we've reached a consensus to add the [[nodiscard]] attribute to the exported interfaces. I keep the [[nodiscard]] attribute on the imported `expected` since I see no harm in leaving it here.



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "wgtmac (via GitHub)" <gi...@apache.org>.
wgtmac commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2664807118

   @pitrou @raulcd Do you have any comment on this PR?


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "wgtmac (via GitHub)" <gi...@apache.org>.
wgtmac commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930469633


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   Is it possible to use the following code to wrap `std::expected`?
   ```
   template <typename T, typename E>
   struct [[nodiscard]] expected : std::expected<T, E> {
       using std::expected<T, E>::expected;
   };
   ```
   
   Or
   
   ```
   namespace iceberg {
       template <typename T, typename E>
       using expected = std::expected<T, E>;
   }
   
   namespace std {
       template <typename T, typename E>
       [[nodiscard]] iceberg::expected<T, E>;
   }
   ```



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "wgtmac (via GitHub)" <gi...@apache.org>.
wgtmac commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930469633


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   Is it possible to use the following code to wrap `std::expected`?
   ```
   template <typename T, typename E>
   struct [[nodiscard]] expected : std::expected<T, E> {
       using std::expected<T, E>::expected;
   };
   ```



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1929975364


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   Oops, yes, `[[nodiscard]]`. 
   
   But then that means we can't switch to std::expected down the line, because we've diverged. 



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1939533979


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   @lidavidm the license checker failed 😞 , do you think it's ok we put the imported copyright to the end of ASF license header?



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "raulcd (via GitHub)" <gi...@apache.org>.
raulcd commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930231246


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   oh! we are not vendoring the full file. Sorry, then I am not sure what the header should be on this specific file. We still might want to add the MIT license on the License section



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "Fokko (via GitHub)" <gi...@apache.org>.
Fokko merged PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "raulcd (via GitHub)" <gi...@apache.org>.
raulcd commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930227269


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   The original code is MIT and the [original license](https://github.com/zeus-cpp/expected/blob/dea2a71cdbdb4cbdd1595a1b9d28a12ec49d0517/LICENSE#L12-L13) says:
   
   > The above copyright notice and this permission notice shall be included in all
   copies or substantial portions of the Software.
   
   From my understanding this header should be the MIT license instead of the ASF 2 license and we probably should add a minor note on the license that the original license is MIT, similar to this example:
   https://github.com/apache/arrow/blob/main/cpp/src/arrow/vendored/datetime/date.h#L4-L33
   
   and:
   https://github.com/apache/arrow/blob/main/LICENSE.txt#L525-L555



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "wgtmac (via GitHub)" <gi...@apache.org>.
wgtmac commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1931466108


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   I agree that it is not a good practice to inherit `std::expected`. 
   
   I'm not sure if this has been discussed already. We can configure `bugprone-unused-return-value` to check specific return type (including both the imported expected type and the std::expected from C++23): https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unused-return-value.html#cmdoption-arg-CheckedReturnTypes.



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1931472832


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   We can check it in our own codebase, but the purpose of the annotation is for downstream consumers, isn't it?



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1940862373


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   Ah, that's better, changed that way(keeping the MIT license as the header).



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "Fokko (via GitHub)" <gi...@apache.org>.
Fokko commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2684638023

   Thanks @zhjwpku for adding this, and the `LICENSE` looks good to me. I don't think we need to update the `NOTICE`, but we can revisit that at the release when we have to do a full pass anyway!


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1939509347


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   Ok, I see that in the example you mentioned, it changed the ASF copyright to the original copyright, makes sense to me, will change, but not sure if the CI will pass though.



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1939502825


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   We have this restriction, which make MIT license header less possible:
   
   ```
   header:
     license:
       spdx-id: Apache-2.0
       copyright-owner: Apache Software Foundation
   ```



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1939495804


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   Hmm, I don't think they should, though. A subset of MIT code is still MIT licensed and should at least keep the original copyright and notice...
   
   Note that for arrow-java an ASF member actually came and fixed a few instances of this: https://github.com/apache/arrow-java/pull/550



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2667321536

   > I'm surprised by the license header. Shouldn't we use the Apache license there and record the provenance elsewhere?
   
   Yes, that was the initial version. I adopted the ASF License and included the zeus-cpp License (MIT) in the NOTICE file. There is a line in the ASF License:
   
   ```
   See the NOTICE file distributed with this work for additional information regarding copyright ownership.
   ```
   
   I'm unsure if this is sufficient. @lidavidm suggested some references from Arrow, which led us to finalize the current version.
   


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930450870


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   ok, do you think we should remove this class level [[nodiscard]] and add function level [[discard]]? I once had a idea adding a `Status` wrapper class around `iceberg::expected`, but I don't think that's a good idea.



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930454500


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   I think we have to use function level `[[nodiscard]]`, yes. We just won't have any lint to check it



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1931355812


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   I think it would technically work but I don't think subclassing STL classes is generally recommended (and I don't think expected is meant to be subclassed) so it's possible an implementation change or a different STL implementation would break us



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "pitrou (via GitHub)" <gi...@apache.org>.
pitrou commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2664936223

   I'm surprised by the license header. Shouldn't we use the Apache license there and record the provenance elsewhere?


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2639011661

   @Fokko @Xuanwo I'd appreciate it if you could review this backport of `std::expected`. While we haven't yet decided whether to use `expected` over `exception`, having this backport ready could be beneficial for future feature implementation.


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930259449


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   Yes, but we don't want to loosen the restriction. New code will still be written after all, and suddenly users will lose an important compile time check.



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1929955037


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   > It was stated that we should not enforce `[[maybe_unused]]` via linter because we could apply it here. Should we do that?
   
   You mean [[nodiscard]]? I changed with that.
   
   > 
   > Also, since stdlib doesn't declare it with that attribute, what will happen when we replace `iceberg::expected` with `std::expected`?
   
   No idea, I hope our enforcement will encourage users to adopt a good code style.
   
   



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "wgtmac (via GitHub)" <gi...@apache.org>.
wgtmac commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1930469633


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   Is it possible to use the following code to wrap `std::expected`?
   ```
   template <typename T, typename E>
   struct [[nodiscard]] expected : std::expected<T, E> {
       using std::expected<T, E>::expected;
   };
   ```



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "zhjwpku (via GitHub)" <gi...@apache.org>.
zhjwpku commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1939509347


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   Ok, I see that in the example you mentioned, it changed the ASF copyright to the original copyright, makes sense to me, will change.



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1929899791


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "iceberg/iceberg_export.h"
+
+namespace iceberg {
+
+namespace expected_detail {
+
+template <class T, template <class...> class Template>
+inline constexpr bool is_specialization_v =
+    false;  // true if and only if T is a specialization of Template
+template <template <class...> class Template, class... Types>
+inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
+
+template <class T, class U>
+using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
+template <class T, class U>
+inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
+
+template <class T>
+inline constexpr bool is_trivially_destructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_destructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_trivially_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_trivially_move_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_nothrow_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_default_constructible_or_void_v =
+    is_void_or_v<T, std::is_default_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_constructible_or_void_v =
+    is_void_or_v<T, std::is_copy_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_move_constructible_or_void_v =
+    is_void_or_v<T, std::is_move_constructible<T>>;
+
+template <class T>
+inline constexpr bool is_copy_assignable_or_void_v =
+    is_void_or_v<T, std::is_copy_assignable<T>>;
+
+template <class T>
+inline constexpr bool is_move_assignable_or_void_v =
+    is_void_or_v<T, std::is_move_assignable<T>>;
+
+template <class From, class To>
+inline constexpr bool is_nothrow_convertible_v =
+    noexcept(static_cast<To>(std::declval<From>()));
+
+}  // namespace expected_detail
+
+template <class E>
+class unexpected;
+
+namespace expected_detail {
+
+template <class E>
+struct is_error_type_valid : std::true_type {
+  static_assert(std::is_object_v<E>, "E must be an object type");
+  static_assert(!std::is_array_v<E>, "E must not be an array");
+  static_assert(!std::is_const_v<E>, "E must not be const");
+  static_assert(!std::is_volatile_v<E>, "E must not be volatile");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
+                "E must not be unexpected");
+};
+template <class T>
+inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
+
+}  // namespace expected_detail
+
+template <class E>
+class ICEBERG_EXPORT unexpected {
+ public:
+  static_assert(expected_detail::is_error_type_valid_v<E>);
+
+  unexpected() = delete;
+  ~unexpected() = default;
+
+  unexpected(const unexpected&) = default;
+  unexpected(unexpected&&) = default;
+
+  template <class... Args,
+            typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
+  constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>)
+      : m_val(std::forward<Args>(args)...) {}
+
+  template <class U, class... Args,
+            typename std::enable_if_t<std::is_constructible_v<
+                E, std::initializer_list<U>&, Args...>>* = nullptr>
+  constexpr explicit unexpected(
+      std::in_place_t, std::initializer_list<U> l,
+      Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
+                                                               std::initializer_list<U>&,
+                                                               Args...>)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  template <class Err = E,
+            typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
+  constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
+      : m_val(std::forward<Err>(e)) {}
+
+  unexpected& operator=(const unexpected&) = default;
+  unexpected& operator=(unexpected&&) = default;
+
+  constexpr const E& error() const& noexcept { return m_val; }
+  constexpr E& error() & noexcept { return m_val; }
+  constexpr E&& error() && noexcept { return std::move(m_val); }
+  constexpr const E&& error() const&& noexcept { return std::move(m_val); }
+
+  constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+    static_assert(std::is_swappable_v<E>, "E must be swappable");
+    using std::swap;
+    swap(m_val, other.m_val);
+  }
+
+  friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
+    x.swap(y);
+  }
+
+  template <class E2>
+  friend constexpr bool operator==(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val == rhs.error())) {
+    return lhs.m_val == rhs.error();
+  }
+  template <class E2>
+  friend constexpr bool operator!=(
+      const unexpected& lhs,
+      const unexpected<E2>& rhs) noexcept(noexcept(lhs.m_val != rhs.error())) {
+    return lhs.m_val != rhs.error();
+  }
+
+ private:
+  E m_val;
+};
+
+// deduction guide
+template <class E>
+unexpected(E) -> unexpected<E>;
+
+struct ICEBERG_EXPORT unexpect_t {
+  explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+template <class E>
+class bad_expected_access;
+
+template <>
+class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
+ public:
+  virtual const char* what() const noexcept override { return "Bad expected access"; }
+
+ protected:
+  bad_expected_access() = default;
+  bad_expected_access(const bad_expected_access&) = default;
+  bad_expected_access(bad_expected_access&&) = default;
+  bad_expected_access& operator=(const bad_expected_access&) = default;
+  bad_expected_access& operator=(bad_expected_access&&) = default;
+};
+
+template <class E>
+class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
+ public:
+  explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
+      : m_val(std::move(e)) {}
+
+  const E& error() const& noexcept { return m_val; }
+  E& error() & noexcept { return m_val; }
+  const E&& error() const&& noexcept { return std::move(m_val); }
+  E&& error() && noexcept { return std::move(m_val); }
+
+ private:
+  E m_val;
+};
+
+template <class T, class E>
+class expected;
+
+namespace expected_detail {
+template <class T>
+
+struct is_value_type_valid : std::true_type {
+  static_assert(!std::is_reference_v<T>, "T must not be a reference");
+  static_assert(!std::is_function_v<T>, "T must not be a function");
+  static_assert(!std::is_array_v<T>,
+                "T must not be an array to meet the requirements of Cpp17Destructible "
+                "when T is not cv-void");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
+                "T must not be in_place_t");
+  static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
+                "T must not be unexpect_t");
+  static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
+                "T must not be unexpected");
+};
+
+template <class T>
+inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
+
+template <class T, class E, class U>
+using enable_forward_t = std::enable_if_t<
+    std::is_constructible_v<T, U> &&
+    !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
+    !std::is_same_v<expected<T, E>, std::remove_cvref_t<U>> &&
+    !expected_detail::is_specialization_v<std::remove_cvref_t<U>, unexpected> &&
+    (!std::is_same_v<std::remove_cv_t<T>, bool> ||  // LWG-3836
+     !expected_detail::is_specialization_v<std::remove_cvref_t<U>, expected>)>;
+
+template <class T, class E, class U, class G, class UF, class GF>
+using enable_from_other_expected_t = std::enable_if_t<
+    std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
+    std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>,  // LWG-3836
+                       std::negation<std::disjunction<
+                           std::is_constructible<T, expected<U, G>&>,
+                           std::is_constructible<T, expected<U, G>>,
+                           std::is_constructible<T, const expected<U, G>&>,
+                           std::is_constructible<T, const expected<U, G>>,
+                           std::is_convertible<expected<U, G>&, T>,
+                           std::is_convertible<expected<U, G>&&, T>,
+                           std::is_convertible<const expected<U, G>&, T>,
+                           std::is_convertible<const expected<U, G>&&, T>,
+                           std::is_constructible<unexpected<E>, expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, expected<U, G>>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                           std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
+
+template <class E, class U, class G, class GF>
+using enable_from_other_void_expected_t =
+    std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
+                     std::negation_v<std::disjunction<
+                         std::is_constructible<unexpected<E>, expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, expected<U, G>>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>&>,
+                         std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
+
+}  // namespace expected_detail
+
+namespace expected_detail {
+
+struct no_init_t {
+  explicit no_init_t() = default;
+};
+inline constexpr no_init_t no_init{};
+
+struct construct_with_invoke_result_t {
+  explicit construct_with_invoke_result_t() = default;
+};
+inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
+
+template <class T>
+struct [[nodiscard]] ReinitGuard {
+  static_assert(std::is_nothrow_move_constructible_v<T>);
+
+  ReinitGuard(ReinitGuard const&) = delete;
+  ReinitGuard& operator=(ReinitGuard const&) = delete;
+
+  constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
+  constexpr ~ReinitGuard() noexcept {
+    if (_target) {
+      std::construct_at(_target, std::move(*_tmp));
+    }
+  }
+  T* _target;
+  T* _tmp;
+};
+
+template <class First, class Second, class... Args>
+constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
+    std::is_nothrow_constructible_v<First, Args...>) {
+  if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+  } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
+    First tmp(std::forward<Args>(args)...);
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+    std::construct_at(std::addressof(new_val), std::move(tmp));
+  } else {
+    Second tmp(std::move(old_val));
+    if constexpr (!std::is_trivially_destructible_v<Second>) {
+      old_val.~Second();
+    }
+
+    expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
+                                               std::addressof(tmp)};
+    std::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
+    guard._target = nullptr;
+  }
+}
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for when `T` and `E` are trivially destructible
+template <class T, class E,
+          bool = is_trivially_destructible_or_void_v<T> &&
+                 std::is_trivially_destructible_v<E>>
+struct storage_base {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+template <class T, class E>
+struct storage_base<T, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept(noexcept(T{})) : m_val(T{}), m_has_val(true) {}
+  constexpr storage_base(no_init_t) noexcept : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
+      noexcept(T(std::forward<Args>(args)...)))
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      std::in_place_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
+      noexcept(E(std::forward<Args>(args)...)))
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(
+      unexpect_t, std::initializer_list<U> il,
+      Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(true) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (m_has_val) {
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        m_val.~T();
+      }
+    } else {
+      if constexpr (!std::is_trivially_destructible_v<E>) {
+        m_unexpect.~E();
+      }
+    }
+  }
+
+  union {
+    T m_val;
+    E m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E>
+struct storage_base<void, E, true> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  ~storage_base() = default;
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E>
+struct storage_base<void, E, false> {
+  storage_base(storage_base const&) = default;
+  storage_base(storage_base&&) = default;
+  storage_base& operator=(storage_base const&) = default;
+  storage_base& operator=(storage_base&&) = default;
+
+  constexpr storage_base() noexcept : m_has_val(true) {}
+
+  constexpr storage_base(no_init_t) noexcept : m_val(), m_has_val(false) {}
+
+  constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
+
+  template <class... Args,
+            std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, Args&&... args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
+                                                     Args&&...>>* = nullptr>
+  constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  // helper ctor for transform_error()
+  template <class Fn, class... Args>
+  constexpr explicit storage_base(construct_with_invoke_result_t, unexpect_t, Fn&& func,
+                                  Args&&... args)  //
+      noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
+                                                   std::forward<Args>(args)...))))
+      : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
+        m_has_val(false) {}
+
+  constexpr ~storage_base() noexcept {
+    if (!m_has_val) {
+      m_unexpect.~E();
+    }
+  }
+
+  struct dummy {};
+
+  union {
+    E m_unexpect;
+    dummy m_val;
+  };
+
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct operations_base : storage_base<T, E> {
+  using storage_base<T, E>::storage_base;
+
+  template <class... Args>
+  constexpr void construct(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<T, Args...>) {
+    std::construct_at(&this->m_val, std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs>
+  constexpr void construct_with(Rhs&& rhs) noexcept(
+      std::is_nothrow_constructible_v<T, Rhs>) {
+    std::construct_at(&this->m_val, std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args) noexcept(
+      std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr T& get() & noexcept { return this->m_val; }
+  constexpr const T& get() const& noexcept { return this->m_val; }
+  constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+  constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
+    return std::move(this->m_val);
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct operations_base<void, E> : storage_base<void, E> {
+  using storage_base<void, E>::storage_base;
+
+  constexpr void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs>
+  constexpr void construct_with(Rhs&&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args>
+  constexpr void construct_error(Args&&... args)  //
+      noexcept(std::is_nothrow_constructible_v<E, Args...>) {
+    std::construct_at(&this->m_unexpect, std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  constexpr E& geterr() & noexcept { return this->m_unexpect; }
+  constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
+  constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+  constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
+    return std::move(this->m_unexpect);
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool Enabled =
+              is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
+          bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
+                           std::is_trivially_copy_constructible_v<E>>
+struct copy_ctor_base : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+};
+
+// This specialization is for when T or E are not copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+  copy_ctor_base(const copy_ctor_base& rhs) = delete;
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
+  using operations_base<T, E>::operations_base;
+
+  ~copy_ctor_base() = default;
+  copy_ctor_base() = default;
+
+  constexpr copy_ctor_base(const copy_ctor_base& rhs)  //
+      noexcept(is_nothrow_copy_constructible_or_void_v<T> &&
+               std::is_nothrow_copy_constructible_v<E>)
+      : operations_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  copy_ctor_base(copy_ctor_base&& rhs) = default;
+  copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
+  copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+template <class T, class E,
+          bool Enabled =
+              is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
+          bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
+                           std::is_trivially_move_constructible_v<E>>
+struct move_ctor_base : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+};
+
+// This specialization is for when T or E are not move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+  move_ctor_base(move_ctor_base&& rhs) = delete;
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+// This specialization is for when T or E are not trivially move constructible
+template <class T, class E>
+struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
+  using copy_ctor_base<T, E>::copy_ctor_base;
+
+  ~move_ctor_base() = default;
+  move_ctor_base() = default;
+  move_ctor_base(const move_ctor_base& rhs) = default;
+
+  constexpr move_ctor_base(move_ctor_base&& rhs)  //
+      noexcept(is_nothrow_move_constructible_or_void_v<T> &&
+               std::is_nothrow_move_constructible_v<E>)
+      : copy_ctor_base<T, E>(no_init) {
+    if (rhs.m_has_val) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+
+  move_ctor_base& operator=(const move_ctor_base& rhs) = default;
+  move_ctor_base& operator=(move_ctor_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_copy_assignable_v =
+    is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
+    std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_copy_assignable_v =
+    is_trivially_copy_constructible_or_void_v<T> &&
+    is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) copy assignment operator
+template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
+struct copy_assign_base : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = default;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+  copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
+  using move_ctor_base<T, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs)  //
+      noexcept(std::is_nothrow_copy_constructible_v<T> &&
+               std::is_nothrow_copy_assignable_v<T> &&
+               std::is_nothrow_copy_constructible_v<E> &&
+               std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = rhs.m_val;
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class E>
+struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
+  using move_ctor_base<void, E>::move_ctor_base;
+
+  ~copy_assign_base() = default;
+  copy_assign_base() = default;
+  copy_assign_base(const copy_assign_base& rhs) = default;
+  copy_assign_base(copy_assign_base&& rhs) = default;
+
+  constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
+      std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = rhs.m_unexpect;
+    }
+    return *this;
+  }
+
+  copy_assign_base& operator=(copy_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+inline constexpr bool is_expected_move_assignable_v =
+    is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
+    std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
+    (is_nothrow_move_constructible_or_void_v<T> ||
+     std::is_nothrow_move_constructible_v<E>);
+
+// LWG-4026
+template <class T, class E>
+inline constexpr bool is_expected_trivially_move_assignable_v =
+    is_trivially_move_constructible_or_void_v<T> &&
+    is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
+    std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
+    std::is_trivially_destructible_v<E>;
+
+// This class manages conditionally having a (possibly trivial) move assignment operator
+template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
+          bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
+struct move_assign_base : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = default;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+  move_assign_base& operator=(move_assign_base&& rhs) = delete;
+};
+
+template <class T, class E>
+struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
+  using copy_assign_base<T, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<T> &&
+               std::is_nothrow_move_assignable_v<T> &&
+               std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      this->m_val = std::move(rhs.m_val);
+    } else if (this->m_has_val) {
+      expected_detail::reinit_expected(this->m_unexpect, this->m_val,
+                                       std::move(rhs.m_unexpect));
+    } else if (rhs.m_has_val) {
+      expected_detail::reinit_expected(this->m_val, this->m_unexpect,
+                                       std::move(rhs.m_val));
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    this->m_has_val = rhs.m_has_val;
+    return *this;
+  }
+};
+
+template <class E>
+struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
+  using copy_assign_base<void, E>::copy_assign_base;
+
+  ~move_assign_base() = default;
+  move_assign_base() = default;
+  move_assign_base(const move_assign_base& rhs) = default;
+  move_assign_base(move_assign_base&& rhs) = default;
+  move_assign_base& operator=(const move_assign_base& rhs) = default;
+
+  constexpr move_assign_base& operator=(move_assign_base&& rhs)  //
+      noexcept(std::is_nothrow_move_constructible_v<E> &&
+               std::is_nothrow_move_assignable_v<E>) {
+    if (this->m_has_val && rhs.m_has_val) {
+      // no-op
+    } else if (this->m_has_val) {
+      std::construct_at(std::addressof(this->m_unexpect), std::move(rhs.m_unexpect));
+      this->m_has_val = false;
+    } else if (rhs.m_has_val) {
+      this->m_unexpect.~E();
+      this->m_has_val = true;
+    } else {
+      this->m_unexpect = std::move(rhs.m_unexpect);
+    }
+    return *this;
+  }
+};
+
+// This is needed to be able to construct the default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit default_constructor_tag() = default;
+};
+
+// default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
+struct default_ctor_base {
+  ~default_ctor_base() = default;
+  default_ctor_base() = default;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E>
+struct default_ctor_base<T, E, false> {
+  ~default_ctor_base() = default;
+  default_ctor_base() = delete;
+  default_ctor_base(default_ctor_base const&) = default;
+  default_ctor_base(default_ctor_base&&) = default;
+  default_ctor_base& operator=(default_ctor_base const&) = default;
+  default_ctor_base& operator=(default_ctor_base&&) = default;
+
+  constexpr explicit default_ctor_base(default_constructor_tag) {}
+};
+
+}  // namespace expected_detail
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+
+template <class T, class E>
+class ICEBERG_EXPORT expected : private expected_detail::move_assign_base<T, E>,
+                                private expected_detail::default_ctor_base<T, E> {

Review Comment:
   It was stated that we should not enforce `[[maybe_unused]]` via linter because we could apply it here. Should we do that?
   
   Also, since stdlib doesn't declare it with that attribute, what will happen when we replace `iceberg::expected` with `std::expected`?



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#issuecomment-2667391713

   An Apache member corrected this in arrow-java by making sure vendored files had their original license header: https://github.com/apache/arrow-java/pull/550


-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org


Re: [PR] backport c++23 std::expected [iceberg-cpp]

Posted by "lidavidm (via GitHub)" <gi...@apache.org>.
lidavidm commented on code in PR #40:
URL: https://github.com/apache/iceberg-cpp/pull/40#discussion_r1940318960


##########
src/iceberg/expected.h:
##########
@@ -0,0 +1,2333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */

Review Comment:
   We can't add to paths-ignore? https://github.com/apache/skywalking-eyes?tab=readme-ov-file#configurations



-- 
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.

To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org

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


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@iceberg.apache.org
For additional commands, e-mail: issues-help@iceberg.apache.org