]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/boost/outcome/experimental/status-code/std_error_code.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / outcome / experimental / status-code / std_error_code.hpp
index f5f3d7dadbbc4bf144f544af996d8ad801142841..e2de8f9ccbaa5d6958a9606ecef02f5216de80e5 100644 (file)
@@ -43,29 +43,38 @@ DEALINGS IN THE SOFTWARE.
 
 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
 
-namespace detail
+class _std_error_code_domain;
+//! A `status_code` representing exactly a `std::error_code`
+using std_error_code = status_code<_std_error_code_domain>;
+
+namespace mixins
 {
-  struct make_std_categories
+  template <class Base> struct mixin<Base, _std_error_code_domain> : public Base
   {
-    static const std::error_category &generic_category() { return std::generic_category(); }
-    static const std::error_category &system_category() { return std::system_category(); }
+    using Base::Base;
+
+    //! Implicit constructor from a `std::error_code`
+    inline mixin(std::error_code ec);
+
+    //! Returns the error code category
+    inline const std::error_category &category() const noexcept;
   };
-}
+}  // namespace mixins
 
-template <class error_code_type, class make_category_types> class _error_code_domain;
-//! A wrapper of `std::error_code`.
-using std_error_code = status_code<_error_code_domain<std::error_code, detail::make_std_categories>>;
 
 /*! The implementation of the domain for `std::error_code` error codes.
-*/
-template <class error_code_type, class make_categories_type> class _error_code_domain : public status_code_domain
+ */
+class _std_error_code_domain final : public status_code_domain
 {
   template <class DomainType> friend class status_code;
   template <class StatusCode> friend class detail::indirecting_domain;
   using _base = status_code_domain;
-  using _status_code = status_code<_error_code_domain>;
+  using _error_code_type = std::error_code;
+  using _error_category_type = std::error_category;
+
+  std::string _name;
 
-  static _base::string_ref _make_string_ref(error_code_type c) noexcept
+  static _base::string_ref _make_string_ref(_error_code_type c) noexcept
   {
     try
     {
@@ -85,103 +94,209 @@ template <class error_code_type, class make_categories_type> class _error_code_d
   }
 
 public:
-  //! The value type of the `std::error_code` code, which is the `std::error_code`
-  using value_type = error_code_type;
+  //! The value type of the `std::error_code` code, which stores the `int` from the `std::error_code`
+  using value_type = int;
   using _base::string_ref;
 
+  //! Returns the error category singleton pointer this status code domain represents
+  const _error_category_type &error_category() const noexcept
+  {
+    auto ptr = 0x223a160d20de97b4 ^ this->id();
+    return *reinterpret_cast<const _error_category_type *>(ptr);
+  }
+
   //! Default constructor
-  constexpr explicit _error_code_domain(typename _base::unique_id_type id = 0x223a160d20de97b4) noexcept : _base(id) {}
-  _error_code_domain(const _error_code_domain &) = default;
-  _error_code_domain(_error_code_domain &&) = default;
-  _error_code_domain &operator=(const _error_code_domain &) = default;
-  _error_code_domain &operator=(_error_code_domain &&) = default;
-  ~_error_code_domain() = default;
+  explicit _std_error_code_domain(const _error_category_type &category) noexcept
+      : _base(0x223a160d20de97b4 ^ reinterpret_cast<_base::unique_id_type>(&category))
+      , _name("std_error_code_domain(")
+  {
+    _name.append(category.name());
+    _name.push_back(')');
+  }
+  _std_error_code_domain(const _std_error_code_domain &) = default;
+  _std_error_code_domain(_std_error_code_domain &&) = default;
+  _std_error_code_domain &operator=(const _std_error_code_domain &) = default;
+  _std_error_code_domain &operator=(_std_error_code_domain &&) = default;
+  ~_std_error_code_domain() = default;
 
-  //! Constexpr singleton getter. Returns constexpr error_code_domain variable.
-  static inline constexpr const _error_code_domain &get();
+  static inline const _std_error_code_domain *get(_error_code_type ec);
 
-  virtual string_ref name() const noexcept override { return string_ref("error_code compatibility domain"); }  // NOLINT
+  virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); }  // NOLINT
 protected:
-  virtual bool _do_failure(const status_code<void> &code) const noexcept override final  // NOLINT
-  {
-    assert(code.domain() == *this);
-    return static_cast<const _status_code &>(code).value().value() != 0;  // NOLINT
-  }
-  virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override final  // NOLINT
+  virtual bool _do_failure(const status_code<void> &code) const noexcept override;
+  virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override;
+  virtual generic_code _generic_code(const status_code<void> &code) const noexcept override;
+  virtual string_ref _do_message(const status_code<void> &code) const noexcept override;
+#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
+  BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override;
+#endif
+};
+
+namespace detail
+{
+  extern inline _std_error_code_domain *std_error_code_domain_from_category(const std::error_category &category)
   {
-    assert(code1.domain() == *this);
-    const auto &c1 = static_cast<const _status_code &>(code1);  // NOLINT
-    const auto &cat1 = c1.value().category();
-    // Are we comparing to another wrapped error_code?
-    if(code2.domain() == *this)
+    static constexpr size_t max_items = 64;
+    static struct storage_t
     {
-      const auto &c2 = static_cast<const _status_code &>(code2);  // NOLINT
-      const auto &cat2 = c2.value().category();
-      // If the error code categories are identical, do literal comparison
-      if(cat1 == cat2)
+      std::atomic<unsigned> _lock;
+      union item_t {
+        int _init;
+        _std_error_code_domain domain;
+        constexpr item_t()
+            : _init(0)
+        {
+        }
+        ~item_t() {}
+      } items[max_items];
+      size_t count{0};
+
+      void lock()
       {
-        return c1.value().value() == c2.value().value();
+        while(_lock.exchange(1, std::memory_order_acquire) != 0)
+          ;
       }
-      // Otherwise fall back onto the _generic_code comparison, which uses default_error_condition()
-      return false;
-    }
-    // Am I an error code with generic category?
-    const auto &generic_category = make_categories_type::generic_category();
-    if(cat1 == generic_category)
-    {
-      // Convert to generic code, and compare that
-      generic_code _c1(static_cast<errc>(c1.value().value()));
-      return _c1 == code2;
-    }
-    // Am I an error code with system category?
-    const auto &system_category = make_categories_type::system_category();
-    if(cat1 == system_category)
-    {
-// Convert to POSIX or Win32 code, and compare that
-#ifdef _WIN32
-      win32_code _c1((win32::DWORD) c1.value().value());
-      return _c1 == code2;
-#elif !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX)
-      posix_code _c1(c1.value().value());
-      return _c1 == code2;
-#endif
-    }
-    return false;
+      void unlock() { _lock.store(0, std::memory_order_release); }
+
+      storage_t() {}
+      ~storage_t()
+      {
+        lock();
+        for(size_t n = 0; n < count; n++)
+        {
+          items[n].domain.~_std_error_code_domain();
+        }
+        unlock();
+      }
+      _std_error_code_domain *add(const std::error_category &category)
+      {
+        _std_error_code_domain *ret = nullptr;
+        lock();
+        for(size_t n = 0; n < count; n++)
+        {
+          if(items[n].domain.error_category() == category)
+          {
+            ret = &items[n].domain;
+            break;
+          }
+        }
+        if(ret == nullptr && count < max_items)
+        {
+          ret = new(&items[count++].domain) _std_error_code_domain(category);
+        }
+        unlock();
+        return ret;
+      }
+    } storage;
+    return storage.add(category);
   }
-  virtual generic_code _generic_code(const status_code<void> &code) const noexcept override final  // NOLINT
+}  // namespace detail
+
+namespace mixins
+{
+  template <class Base>
+  inline mixin<Base, _std_error_code_domain>::mixin(std::error_code ec)
+      : Base(typename Base::_value_type_constructor{}, _std_error_code_domain::get(ec), ec.value())
   {
-    assert(code.domain() == *this);
-    const auto &c = static_cast<const _status_code &>(code);  // NOLINT
-    // Ask my embedded error code for its mapping to std::errc, which is a subset of our generic_code errc.
-    return generic_code(static_cast<errc>(c.value().default_error_condition().value()));
   }
-  virtual string_ref _do_message(const status_code<void> &code) const noexcept override  // NOLINT
+
+  template <class Base> inline const std::error_category &mixin<Base, _std_error_code_domain>::category() const noexcept
+  {
+    const auto &domain = static_cast<const _std_error_code_domain &>(this->domain());
+    return domain.error_category();
+  };
+}  // namespace mixins
+
+inline const _std_error_code_domain *_std_error_code_domain::get(std::error_code ec)
+{
+  auto *p = detail::std_error_code_domain_from_category(ec.category());
+  assert(p != nullptr);
+  if(p == nullptr)
   {
-    assert(code.domain() == *this);
-    const auto &c = static_cast<const _status_code &>(code);  // NOLINT
-    return _make_string_ref(c.value());
+    abort();
   }
-#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
-  BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override  // NOLINT
+  return p;
+}
+
+
+inline bool _std_error_code_domain::_do_failure(const status_code<void> &code) const noexcept
+{
+  assert(code.domain() == *this);
+  return static_cast<const std_error_code &>(code).value() != 0;  // NOLINT
+}
+
+inline bool _std_error_code_domain::_do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept
+{
+  assert(code1.domain() == *this);
+  const auto &c1 = static_cast<const std_error_code &>(code1);  // NOLINT
+  const auto &cat1 = c1.category();
+  // Are we comparing to another wrapped error_code?
+  if(code2.domain() == *this)
   {
-    assert(code.domain() == *this);
-    const auto &c = static_cast<const _status_code &>(code);  // NOLINT
-    throw std::system_error(c.value());
+    const auto &c2 = static_cast<const std_error_code &>(code2);  // NOLINT
+    const auto &cat2 = c2.category();
+    // If the error code categories are identical, do literal comparison
+    if(cat1 == cat2)
+    {
+      return c1.value() == c2.value();
+    }
+    // Otherwise fall back onto the _generic_code comparison, which uses default_error_condition()
+    return false;
+  }
+  // Am I an error code with generic category?
+  if(cat1 == std::generic_category())
+  {
+    // Convert to generic code, and compare that
+    generic_code _c1(static_cast<errc>(c1.value()));
+    return _c1 == code2;
   }
+  // Am I an error code with system category?
+  if(cat1 == std::system_category())
+  {
+// Convert to POSIX or Win32 code, and compare that
+#ifdef _WIN32
+    win32_code _c1((win32::DWORD) c1.value());
+    return _c1 == code2;
+#elif !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX)
+    posix_code _c1(c1.value());
+    return _c1 == code2;
 #endif
-};
-//! A constexpr source variable for the `std::error_code` code domain. Returned by `_error_code_domain<error_code_type, detail::make_std_categoriesy>::get()`.
-constexpr _error_code_domain<std::error_code, detail::make_std_categories> std_error_code_domain;
-template <class error_code_type, class make_categories_type> inline constexpr const _error_code_domain<error_code_type, make_categories_type> &_error_code_domain<error_code_type, make_categories_type>::get()
+  }
+  return false;
+}
+
+inline generic_code _std_error_code_domain::_generic_code(const status_code<void> &code) const noexcept
 {
-  return std_error_code_domain;
+  assert(code.domain() == *this);
+  const auto &c = static_cast<const std_error_code &>(code);  // NOLINT
+  // Ask my embedded error code for its mapping to std::errc, which is a subset of our generic_code errc.
+  return generic_code(static_cast<errc>(c.category().default_error_condition(c.value()).value()));
 }
-// Enable implicit construction of `std_error_code` from `std::error_code`.
-inline std_error_code make_status_code(std::error_code c) noexcept
+
+inline _std_error_code_domain::string_ref _std_error_code_domain::_do_message(const status_code<void> &code) const noexcept
 {
-  return std_error_code(in_place, c);
+  assert(code.domain() == *this);
+  const auto &c = static_cast<const std_error_code &>(code);  // NOLINT
+  return _make_string_ref(_error_code_type(c.value(), c.category()));
 }
 
+#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
+BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN inline void _std_error_code_domain::_do_throw_exception(const status_code<void> &code) const
+{
+  assert(code.domain() == *this);
+  const auto &c = static_cast<const std_error_code &>(code);  // NOLINT
+  throw std::system_error(std::error_code(c.value(), c.category()));
+}
+#endif
+
+static_assert(sizeof(std_error_code) <= sizeof(void *) * 2, "std_error_code does not fit into a system_code!");
+
 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
 
+// Enable implicit construction of `std_error_code` from `std::error_code`.
+namespace std
+{
+  inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code make_status_code(error_code c) noexcept { return BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code(c); }
+}  // namespace std
+
 #endif