Seastar
High performance C++ framework for concurrent servers
noncopyable_function.hh
1 /*
2  * This file is open source software, licensed to you under the terms
3  * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4  * distributed with this work for additional information regarding copyright
5  * ownership. You may not use this file except in compliance with the License.
6  *
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an
13  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14  * KIND, either express or implied. See the License for the
15  * specific language governing permissions and limitations
16  * under the License.
17  */
18 /*
19  * Copyright (C) 2017 ScyllaDB Ltd.
20  */
21 
22 #pragma once
23 
24 #include <seastar/util/modules.hh>
25 #include <seastar/util/used_size.hh>
26 #include <seastar/util/concepts.hh>
27 
28 #ifndef SEASTAR_MODULE
29 #include <utility>
30 #include <type_traits>
31 #include <functional>
32 #endif
33 
34 namespace seastar {
35 
36 template <typename Signature>
38 
39 namespace internal {
40 
41 class noncopyable_function_base {
42 private:
43  noncopyable_function_base() = default;
44  static constexpr size_t nr_direct = 32;
45  union [[gnu::may_alias]] storage {
46  char direct[nr_direct];
47  void* indirect;
48  };
49  using move_type = void (*)(noncopyable_function_base* from, noncopyable_function_base* to);
50  using destroy_type = void (*)(noncopyable_function_base* func);
51 
52  static void empty_move(noncopyable_function_base*, noncopyable_function_base*) {}
53  static void empty_destroy(noncopyable_function_base*) {}
54 
55  static void indirect_move(noncopyable_function_base* from, noncopyable_function_base* to) {
56  using void_ptr = void*;
57  new (&to->_storage.indirect) void_ptr(from->_storage.indirect);
58  }
59 
60  template <size_t N>
61  static void trivial_direct_move(noncopyable_function_base* from, noncopyable_function_base* to) {
62  // We use bytewise copy here since we lost the type. This means that
63  // we will copy any holes/padding not initialized by the move
64  // constructor in direct_vtable_for::initialize(). This is okay,
65  // since we won't use those holes/padding, but gcc doesn't know
66  // that, and complains. Silence it.
67 #pragma GCC diagnostic push
68 #pragma GCC diagnostic ignored "-Wuninitialized"
69  // Avoid including <algorithm> just for this
70  for (unsigned i = 0; i != N; ++i) {
71  to->_storage.direct[i] = from->_storage.direct[i];
72  }
73 #pragma GCC diagnostic pop
74  }
75 
76  static void trivial_direct_destroy(noncopyable_function_base*) {
77  }
78 
79 private:
80  storage _storage;
81 
82  template <typename Signature>
83  friend class seastar::noncopyable_function;
84 };
85 
86 template<typename FirstArg = void, typename... RemainingArgs>
87 struct is_nothrow_if_object {
88  static constexpr bool value = is_nothrow_if_object<FirstArg>::value && is_nothrow_if_object<RemainingArgs...>::value;
89 };
90 
91 template<typename Arg>
92 struct is_nothrow_if_object<Arg> {
93  static constexpr bool value = !std::is_object_v<Arg> || std::is_nothrow_move_constructible_v<Arg>;
94 };
95 
96 template<>
97 struct is_nothrow_if_object<> {
98  static constexpr bool value = true;
99 };
100 
101 }
102 
105 SEASTAR_MODULE_EXPORT
106 template <typename Ret, typename... Args, bool Noexcept>
107 class noncopyable_function<Ret (Args...) noexcept(Noexcept)> : private internal::noncopyable_function_base {
108  using call_type = Ret (*)(const noncopyable_function* func, Args...);
109  struct vtable {
110  const call_type call;
111  const move_type move;
112  const destroy_type destroy;
113  };
114 private:
115  const vtable* _vtable;
116 private:
117  static Ret empty_call(const noncopyable_function*, [[maybe_unused]] Args... args) {
118  throw std::bad_function_call();
119  }
120 
121  static constexpr vtable _s_empty_vtable = {empty_call, empty_move, empty_destroy};
122 
123  template <typename Func>
124  struct direct_vtable_for {
125  static Func* access(noncopyable_function* func) { return reinterpret_cast<Func*>(func->_storage.direct); }
126  static const Func* access(const noncopyable_function* func) { return reinterpret_cast<const Func*>(func->_storage.direct); }
127  static Func* access(noncopyable_function_base* func) { return access(static_cast<noncopyable_function*>(func)); }
128  static Ret call(const noncopyable_function* func, Args... args) noexcept(Noexcept) {
129  return (*access(const_cast<noncopyable_function*>(func)))(std::forward<Args>(args)...);
130  }
131  static void move(noncopyable_function_base* from, noncopyable_function_base* to) {
132  new (access(to)) Func(std::move(*access(from)));
133  destroy(from);
134  }
135  static constexpr move_type select_move_thunk() {
136  bool can_trivially_move = std::is_trivially_move_constructible_v<Func>
137  && std::is_trivially_destructible_v<Func>;
138  return can_trivially_move ? trivial_direct_move<internal::used_size<Func>::value> : move;
139  }
140  static void destroy(noncopyable_function_base* func) {
141  access(func)->~Func();
142  }
143  static constexpr destroy_type select_destroy_thunk() {
144  return std::is_trivially_destructible_v<Func> ? trivial_direct_destroy : destroy;
145  }
146  static void initialize(Func&& from, noncopyable_function* to) {
147  new (access(to)) Func(std::move(from));
148  }
149  static constexpr vtable make_vtable() { return { call, select_move_thunk(), select_destroy_thunk() }; }
150  static const vtable s_vtable;
151  };
152  template <typename Func>
153  struct indirect_vtable_for {
154  static Func* access(noncopyable_function* func) { return reinterpret_cast<Func*>(func->_storage.indirect); }
155  static const Func* access(const noncopyable_function* func) { return reinterpret_cast<const Func*>(func->_storage.indirect); }
156  static Func* access(noncopyable_function_base* func) { return access(static_cast<noncopyable_function*>(func)); }
157  static Ret call(const noncopyable_function* func, Args... args) noexcept(Noexcept) {
158  return (*access(const_cast<noncopyable_function*>(func)))(std::forward<Args>(args)...);
159  }
160  static void destroy(noncopyable_function_base* func) {
161  delete access(func);
162  }
163  static void initialize(Func&& from, noncopyable_function* to) {
164  to->_storage.indirect = new Func(std::move(from));
165  }
166  static constexpr vtable make_vtable() { return { call, indirect_move, destroy }; }
167  static const vtable s_vtable;
168  };
169  template <typename Func, bool Direct = true>
170  struct select_vtable_for : direct_vtable_for<Func> {};
171  template <typename Func>
172  struct select_vtable_for<Func, false> : indirect_vtable_for<Func> {};
173  template <typename Func>
174  static constexpr bool is_direct() {
175  return sizeof(Func) <= nr_direct && alignof(Func) <= alignof(storage)
176  && std::is_nothrow_move_constructible_v<Func>;
177  }
178  template <typename Func>
179  struct vtable_for : select_vtable_for<Func, is_direct<Func>()> {};
180 public:
181  noncopyable_function() noexcept : _vtable(&_s_empty_vtable) {}
182  template <typename Func>
183  SEASTAR_CONCEPT( requires std::is_invocable_r_v<Ret, Func, Args...> )
184  noncopyable_function(Func func) {
185  static_assert(!Noexcept || noexcept(std::declval<Func>()(std::declval<Args>()...)));
186  vtable_for<Func>::initialize(std::move(func), this);
187  _vtable = &vtable_for<Func>::s_vtable;
188  }
189  template <typename Object, typename... AllButFirstArg>
190  noncopyable_function(Ret (Object::*member)(AllButFirstArg...) noexcept(Noexcept)) : noncopyable_function(std::mem_fn(member)) {}
191  template <typename Object, typename... AllButFirstArg>
192  noncopyable_function(Ret (Object::*member)(AllButFirstArg...) const noexcept(Noexcept)) : noncopyable_function(std::mem_fn(member)) {}
193 
195  _vtable->destroy(this);
196  }
197 
199  noncopyable_function& operator=(const noncopyable_function&) = delete;
200 
201  noncopyable_function(noncopyable_function&& x) noexcept : _vtable(std::exchange(x._vtable, &_s_empty_vtable)) {
202  _vtable->move(&x, this);
203  }
204 
205  noncopyable_function& operator=(noncopyable_function&& x) noexcept {
206  if (this != &x) {
207  this->~noncopyable_function();
208  new (this) noncopyable_function(std::move(x));
209  }
210  return *this;
211  }
212 
213  Ret operator()(Args... args) const noexcept(Noexcept) {
214  static_assert(!Noexcept || internal::is_nothrow_if_object<Args...>::value);
215  return _vtable->call(this, std::forward<Args>(args)...);
216  }
217 
218  explicit operator bool() const {
219  return _vtable != &_s_empty_vtable;
220  }
221 };
222 
223 
224 template <typename Ret, typename... Args, bool Noexcept>
225 template <typename Func>
226 const typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::direct_vtable_for<Func>::s_vtable
227  = noncopyable_function<Ret (Args...) noexcept(Noexcept)>::direct_vtable_for<Func>::make_vtable();
228 
229 
230 template <typename Ret, typename... Args, bool Noexcept>
231 template <typename Func>
232 const typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::indirect_vtable_for<Func>::s_vtable
233  = noncopyable_function<Ret (Args...) noexcept(Noexcept)>::indirect_vtable_for<Func>::make_vtable();
234 
235 }
236 
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Definition: noncopyable_function.hh:37