]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
common: Add bind_allocator template
authorAdam C. Emerson <aemerson@redhat.com>
Fri, 21 Feb 2020 17:45:01 +0000 (12:45 -0500)
committerAdam C. Emerson <aemerson@redhat.com>
Wed, 9 Sep 2020 02:09:40 +0000 (22:09 -0400)
Bind an allocator to a completion, analogously to
boost::async::bind_executor.

Based on bind_executor.hpp which is copyright 2003-2019
Christopher M. Kohlhoff <chris@kohlhoff.com> and under
the Boost Software License, Version 1.0

Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
COPYING
src/common/async/bind_allocator.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
index 7dba4af7faa8addc4dc2551aa25d5d18186ac49f..253bae48ea87a50359ab09d290515afa42658ea0 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -196,3 +196,12 @@ License: Boost Software License, Version 1.0
 Files: src/include/expected.hpp
 Copyright: 2017, Simon Brand
 License: CC0
+
+Files: src/include/uses_allocator.h
+Copyright: 2016, Pablo Halpern <phalpern@halpernwightsoftware.com>
+License: Boost Software License, Version 1.0
+
+Files: src/common/async/bind_allocator.h
+Copyright: 2020 Red Hat <contact@redhat.com>
+           2003-2019 Christopher M. Kohlhoff <chris@kohlhoff.com>
+License: Boost Software License, Version 1.0
diff --git a/src/common/async/bind_allocator.h b/src/common/async/bind_allocator.h
new file mode 100644 (file)
index 0000000..0e4b82f
--- /dev/null
@@ -0,0 +1,244 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+// Based on bind_executor.h from Boost.Asio which is Copyright (c)
+// 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/execution_context.hpp>
+
+#include "include/uses_allocator.h"
+
+namespace ceph::async {
+namespace detail {
+template<typename T>
+struct allocator_binder_check
+{
+  typedef void type;
+};
+
+// Helper to:
+// - Apply the empty base optimization to the allocator.
+// - Perform uses_allocator construction of the target type, if required.
+
+template<typename T, typename Allocator, bool UsesAllocator>
+class allocator_binder_base;
+
+template<typename T, typename Allocator>
+class allocator_binder_base<T, Allocator, true>
+  : protected Allocator
+{
+protected:
+  template<typename A, typename U>
+  allocator_binder_base(A&& a, U&& u)
+    : Allocator(std::forward<A>(a),
+               target(ceph::make_obj_using_allocator(*this,
+                                                     std::forward<U>(u)))) {}
+
+  T target;
+};
+
+template <typename T, typename Allocator>
+class allocator_binder_base<T, Allocator, false>
+  : protected Allocator
+
+{
+protected:
+  template<typename A, typename U>
+  allocator_binder_base(A&& a, U&& u)
+    : Allocator(std::forward<A>(a)),
+      target(std::forward<U>(u)) {}
+
+  T target;
+};
+
+// Helper to enable SFINAE on zero-argument operator() below.
+
+template<typename T, typename = void>
+struct allocator_binder_result_of0
+{
+  using type = void;
+};
+
+template<typename T>
+struct allocator_binder_result_of0<
+  T, typename allocator_binder_check<std::result_of_t<T()>>::type>
+{
+  using type = typename std::result_of_t<T()>;
+};
+} // namespace detail
+
+/// A call wrapper type to bind an allocator of type @c Allocator to
+/// an object of type @c T.
+template<typename T, typename Allocator>
+class allocator_binder
+  : private detail::allocator_binder_base<T, Allocator,
+                                         std::uses_allocator<T, Allocator>
+                                         ::value>
+{
+public:
+  /// The type of the target object.
+  using target_type = T;
+
+  /// The type of the associated allocator.
+  using allocator_type = Allocator;
+
+  /// Construct an allocator wrapper for the specified object.
+  /**
+   * This constructor is only valid if the type @c T is constructible from type
+   * @c U.
+   */
+  template<typename U>
+  allocator_binder(std::allocator_arg_t, const allocator_type& a,
+                  U&& u)
+    : base_type(a, std::forward<U>(u)) {}
+
+  /// Copy constructor.
+  allocator_binder(const allocator_binder& other)
+    : base_type(other.get_allocator(), other.get()) {}
+
+  /// Construct a copy, but specify a different allocator.
+  allocator_binder(std::allocator_arg_t, const allocator_type& e,
+                  const allocator_binder& other)
+    : base_type(e, other.get()) {}
+
+  /// Construct a copy of a different allocator wrapper type.
+  /**
+   * This constructor is only valid if the @c Allocator type is
+   * constructible from type @c OtherAllocator, and the type @c T is
+   * constructible from type @c U.
+   */
+  template<typename U, typename OtherAllocator>
+  allocator_binder(const allocator_binder<U, OtherAllocator>& other)
+    : base_type(other.get_allocator(), other.get()) {}
+
+  /// Construct a copy of a different allocator wrapper type, but specify a
+  /// different allocator.
+  /**
+   * This constructor is only valid if the type @c T is constructible from type
+   * @c U.
+   */
+  template<typename U, typename OtherAllocator>
+  allocator_binder(std::allocator_arg_t, const allocator_type& a,
+                  const allocator_binder<U, OtherAllocator>& other)
+    : base_type(a, other.get()) {}
+
+  /// Move constructor.
+  allocator_binder(allocator_binder&& other)
+    : base_type(std::move(other.get_allocator()),
+               std::move(other.get())) {}
+
+  /// Move construct the target object, but specify a different allocator.
+  allocator_binder(std::allocator_arg_t, const allocator_type& e,
+                  allocator_binder&& other)
+    : base_type(e, std::move(other.get())) {}
+
+  /// Move construct from a different allocator wrapper type.
+  template<typename U, typename OtherAllocator>
+  allocator_binder(allocator_binder<U, OtherAllocator>&& other)
+    : base_type(std::move(other.get_allocator()), std::move(other.get())) {}
+
+  /// Move construct from a different allocator wrapper type, but specify a
+  /// different allocator.
+  template<typename U, typename OtherAllocator>
+  allocator_binder(std::allocator_arg_t, const allocator_type& a,
+                  allocator_binder<U, OtherAllocator>&& other)
+    : base_type(a, std::move(other.get())) {}
+
+  /// Destructor.
+  ~allocator_binder() = default;
+
+  /// Obtain a reference to the target object.
+  target_type& get() noexcept
+  {
+    return this->target;
+  }
+
+  /// Obtain a reference to the target object.
+  const target_type& get() const noexcept
+  {
+    return this->target;
+  }
+
+  /// Obtain the associated allocator.
+  allocator_type get_allocator() const noexcept {
+    return static_cast<const Allocator&>(*this);
+  }
+
+  /// Forwarding function call operator.
+  template<typename... Args>
+  decltype(auto) operator()(Args&&... args) {
+    return this->target(std::forward<Args>(args)...);
+  }
+
+  /// Forwarding function call operator.
+  template<typename... Args>
+  decltype(auto) operator()(Args&&... args) const {
+    return this->target(std::forward<Args>(args)...);
+  }
+
+
+private:
+  using base_type =
+    detail::allocator_binder_base<T, Allocator,
+                                 std::uses_allocator_v<T, Allocator>>;
+};
+
+/// Associate an object of type @c T with an allocator of type @c Allocator.
+template<typename Allocator, typename T>
+inline allocator_binder<typename std::decay_t<T>, Allocator>
+bind_allocator(const Allocator& a, T&& t)
+{
+  return allocator_binder<std::decay_t<T>, Allocator>(std::allocator_arg_t(),
+                                                     a, std::forward<T>(t));
+}
+} // namespace ceph::async
+
+// Since we have an allocator_type member we shouldn't need a
+// uses_allocator specialization.
+
+namespace boost::asio {
+template<typename T, typename Allocator, typename Signature>
+class async_result<ceph::async::allocator_binder<T, Allocator>, Signature>
+{
+public:
+  using completion_handler_type =
+    ceph::async::allocator_binder<
+  typename async_result<T, Signature>::completion_handler_type, Allocator>;
+
+  using return_type = typename async_result<T, Signature>::return_type;
+
+  explicit async_result(ceph::async::allocator_binder<T, Allocator>& b)
+    : target(b.get()) {}
+
+  return_type get() {
+    return target.get();
+  }
+
+private:
+  async_result(const async_result&) = delete;
+  async_result& operator=(const async_result&) = delete;
+
+  async_result<T, Signature> target;
+};
+
+template<typename T, typename Allocator, typename Executor>
+struct associated_executor<ceph::async::allocator_binder<T, Allocator>,
+                          Executor>
+{
+  using type = typename associated_executor<T, Executor>::type;
+
+  static type get(const ceph::async::allocator_binder<T, Allocator>& b,
+                 Executor ex = {}) noexcept {
+    return get_associated_executor(b.get(), ex);
+  }
+};
+} // namespace boost::asio