]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/util/noncopyable_function.hh
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / include / seastar / util / 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/used_size.hh>
25 #include <seastar/util/concepts.hh>
26 #include <utility>
27 #include <type_traits>
28 #include <functional>
29
30 namespace seastar {
31
32 template <typename Signature>
33 class noncopyable_function;
34
35 namespace internal {
36
37 class noncopyable_function_base {
38 private:
39 noncopyable_function_base() = default;
40 static constexpr size_t nr_direct = 32;
41 union [[gnu::may_alias]] storage {
42 char direct[nr_direct];
43 void* indirect;
44 };
45 using move_type = void (*)(noncopyable_function_base* from, noncopyable_function_base* to);
46 using destroy_type = void (*)(noncopyable_function_base* func);
47
48 static void empty_move(noncopyable_function_base*, noncopyable_function_base*) {}
49 static void empty_destroy(noncopyable_function_base*) {}
50
51 static void indirect_move(noncopyable_function_base* from, noncopyable_function_base* to) {
52 using void_ptr = void*;
53 new (&to->_storage.indirect) void_ptr(from->_storage.indirect);
54 }
55
56 template <size_t N>
57 static void trivial_direct_move(noncopyable_function_base* from, noncopyable_function_base* to) {
58 // We use bytewise copy here since we lost the type. This means that
59 // we will copy any holes/padding not initialized by the move
60 // constructor in direct_vtable_for::initialize(). This is okay,
61 // since we won't use those holes/padding, but gcc doesn't know
62 // that, and complains. Silence it.
63 #pragma GCC diagnostic push
64 #pragma GCC diagnostic ignored "-Wuninitialized"
65 // Avoid including <algorithm> just for this
66 for (unsigned i = 0; i != N; ++i) {
67 to->_storage.direct[i] = from->_storage.direct[i];
68 }
69 #pragma GCC diagnostic pop
70 }
71
72 static void trivial_direct_destroy(noncopyable_function_base*) {
73 }
74
75 private:
76 storage _storage;
77
78 template <typename Signature>
79 friend class seastar::noncopyable_function;
80 };
81
82 template<typename FirstArg = void, typename... RemainingArgs>
83 struct is_nothrow_if_object {
84 static constexpr bool value = is_nothrow_if_object<FirstArg>::value && is_nothrow_if_object<RemainingArgs...>::value;
85 };
86
87 template<typename Arg>
88 struct is_nothrow_if_object<Arg> {
89 static constexpr bool value = !std::is_object<Arg>::value || std::is_nothrow_move_constructible<Arg>::value;
90 };
91
92 template<>
93 struct is_nothrow_if_object<> {
94 static constexpr bool value = true;
95 };
96
97 }
98
99 /// A clone of \c std::function, but only invokes the move constructor
100 /// of the contained function.
101 template <typename Ret, typename... Args, bool Noexcept>
102 class noncopyable_function<Ret (Args...) noexcept(Noexcept)> : private internal::noncopyable_function_base {
103 using call_type = Ret (*)(const noncopyable_function* func, Args...);
104 struct vtable {
105 const call_type call;
106 const move_type move;
107 const destroy_type destroy;
108 };
109 private:
110 const vtable* _vtable;
111 private:
112 static Ret empty_call(const noncopyable_function*, [[maybe_unused]] Args... args) {
113 throw std::bad_function_call();
114 }
115
116 static constexpr vtable _s_empty_vtable = {empty_call, empty_move, empty_destroy};
117
118 template <typename Func>
119 struct direct_vtable_for {
120 static Func* access(noncopyable_function* func) { return reinterpret_cast<Func*>(func->_storage.direct); }
121 static const Func* access(const noncopyable_function* func) { return reinterpret_cast<const Func*>(func->_storage.direct); }
122 static Func* access(noncopyable_function_base* func) { return access(static_cast<noncopyable_function*>(func)); }
123 static Ret call(const noncopyable_function* func, Args... args) noexcept(Noexcept) {
124 return (*access(const_cast<noncopyable_function*>(func)))(std::forward<Args>(args)...);
125 }
126 static void move(noncopyable_function_base* from, noncopyable_function_base* to) {
127 new (access(to)) Func(std::move(*access(from)));
128 destroy(from);
129 }
130 static constexpr move_type select_move_thunk() {
131 bool can_trivially_move = std::is_trivially_move_constructible<Func>::value
132 && std::is_trivially_destructible<Func>::value;
133 return can_trivially_move ? trivial_direct_move<internal::used_size<Func>::value> : move;
134 }
135 static void destroy(noncopyable_function_base* func) {
136 access(func)->~Func();
137 }
138 static constexpr destroy_type select_destroy_thunk() {
139 return std::is_trivially_destructible<Func>::value ? trivial_direct_destroy : destroy;
140 }
141 static void initialize(Func&& from, noncopyable_function* to) {
142 new (access(to)) Func(std::move(from));
143 }
144 static constexpr vtable make_vtable() { return { call, select_move_thunk(), select_destroy_thunk() }; }
145 static const vtable s_vtable;
146 };
147 template <typename Func>
148 struct indirect_vtable_for {
149 static Func* access(noncopyable_function* func) { return reinterpret_cast<Func*>(func->_storage.indirect); }
150 static const Func* access(const noncopyable_function* func) { return reinterpret_cast<const Func*>(func->_storage.indirect); }
151 static Func* access(noncopyable_function_base* func) { return access(static_cast<noncopyable_function*>(func)); }
152 static Ret call(const noncopyable_function* func, Args... args) noexcept(Noexcept) {
153 return (*access(const_cast<noncopyable_function*>(func)))(std::forward<Args>(args)...);
154 }
155 static void destroy(noncopyable_function_base* func) {
156 delete access(func);
157 }
158 static void initialize(Func&& from, noncopyable_function* to) {
159 to->_storage.indirect = new Func(std::move(from));
160 }
161 static constexpr vtable make_vtable() { return { call, indirect_move, destroy }; }
162 static const vtable s_vtable;
163 };
164 template <typename Func, bool Direct = true>
165 struct select_vtable_for : direct_vtable_for<Func> {};
166 template <typename Func>
167 struct select_vtable_for<Func, false> : indirect_vtable_for<Func> {};
168 template <typename Func>
169 static constexpr bool is_direct() {
170 return sizeof(Func) <= nr_direct && alignof(Func) <= alignof(storage)
171 && std::is_nothrow_move_constructible<Func>::value;
172 }
173 template <typename Func>
174 struct vtable_for : select_vtable_for<Func, is_direct<Func>()> {};
175 public:
176 noncopyable_function() noexcept : _vtable(&_s_empty_vtable) {}
177 template <typename Func>
178 SEASTAR_CONCEPT( requires std::is_invocable_r_v<Ret, Func, Args...> )
179 noncopyable_function(Func func) {
180 static_assert(!Noexcept || noexcept(std::declval<Func>()(std::declval<Args>()...)));
181 vtable_for<Func>::initialize(std::move(func), this);
182 _vtable = &vtable_for<Func>::s_vtable;
183 }
184 template <typename Object, typename... AllButFirstArg>
185 noncopyable_function(Ret (Object::*member)(AllButFirstArg...) noexcept(Noexcept)) : noncopyable_function(std::mem_fn(member)) {}
186 template <typename Object, typename... AllButFirstArg>
187 noncopyable_function(Ret (Object::*member)(AllButFirstArg...) const noexcept(Noexcept)) : noncopyable_function(std::mem_fn(member)) {}
188
189 ~noncopyable_function() {
190 _vtable->destroy(this);
191 }
192
193 noncopyable_function(const noncopyable_function&) = delete;
194 noncopyable_function& operator=(const noncopyable_function&) = delete;
195
196 noncopyable_function(noncopyable_function&& x) noexcept : _vtable(std::exchange(x._vtable, &_s_empty_vtable)) {
197 _vtable->move(&x, this);
198 }
199
200 noncopyable_function& operator=(noncopyable_function&& x) noexcept {
201 if (this != &x) {
202 this->~noncopyable_function();
203 new (this) noncopyable_function(std::move(x));
204 }
205 return *this;
206 }
207
208 Ret operator()(Args... args) const noexcept(Noexcept) {
209 static_assert(!Noexcept || internal::is_nothrow_if_object<Args...>::value);
210 return _vtable->call(this, std::forward<Args>(args)...);
211 }
212
213 explicit operator bool() const {
214 return _vtable != &_s_empty_vtable;
215 }
216 };
217
218
219 template <typename Ret, typename... Args, bool Noexcept>
220 constexpr typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::_s_empty_vtable;
221
222 template <typename Ret, typename... Args, bool Noexcept>
223 template <typename Func>
224 const typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::direct_vtable_for<Func>::s_vtable
225 = noncopyable_function<Ret (Args...) noexcept(Noexcept)>::direct_vtable_for<Func>::make_vtable();
226
227
228 template <typename Ret, typename... Args, bool Noexcept>
229 template <typename Func>
230 const typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::indirect_vtable_for<Func>::s_vtable
231 = noncopyable_function<Ret (Args...) noexcept(Noexcept)>::indirect_vtable_for<Func>::make_vtable();
232
233 }
234