]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/util/noncopyable_function.hh
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / seastar / include / seastar / util / noncopyable_function.hh
CommitLineData
11fdf7f2
TL
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
9f95a23c 24#include <seastar/util/used_size.hh>
1e59de90 25#include <seastar/util/concepts.hh>
11fdf7f2
TL
26#include <utility>
27#include <type_traits>
28#include <functional>
29
30namespace seastar {
31
32template <typename Signature>
33class noncopyable_function;
34
9f95a23c
TL
35namespace internal {
36
37class noncopyable_function_base {
38private:
39 noncopyable_function_base() = default;
11fdf7f2
TL
40 static constexpr size_t nr_direct = 32;
41 union [[gnu::may_alias]] storage {
42 char direct[nr_direct];
43 void* indirect;
44 };
9f95a23c
TL
45 using move_type = void (*)(noncopyable_function_base* from, noncopyable_function_base* to);
46 using destroy_type = void (*)(noncopyable_function_base* func);
47
1e59de90
TL
48 static void empty_move(noncopyable_function_base*, noncopyable_function_base*) {}
49 static void empty_destroy(noncopyable_function_base*) {}
9f95a23c
TL
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) {
20effc67
TL
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"
9f95a23c
TL
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 }
20effc67 69#pragma GCC diagnostic pop
9f95a23c
TL
70 }
71
1e59de90 72 static void trivial_direct_destroy(noncopyable_function_base*) {
9f95a23c
TL
73 }
74
75private:
76 storage _storage;
77
78 template <typename Signature>
79 friend class seastar::noncopyable_function;
80};
81
f67539c2
TL
82template<typename FirstArg = void, typename... RemainingArgs>
83struct is_nothrow_if_object {
84 static constexpr bool value = is_nothrow_if_object<FirstArg>::value && is_nothrow_if_object<RemainingArgs...>::value;
85};
86
87template<typename Arg>
88struct is_nothrow_if_object<Arg> {
89 static constexpr bool value = !std::is_object<Arg>::value || std::is_nothrow_move_constructible<Arg>::value;
90};
91
92template<>
93struct is_nothrow_if_object<> {
94 static constexpr bool value = true;
95};
96
9f95a23c
TL
97}
98
99/// A clone of \c std::function, but only invokes the move constructor
100/// of the contained function.
f67539c2
TL
101template <typename Ret, typename... Args, bool Noexcept>
102class noncopyable_function<Ret (Args...) noexcept(Noexcept)> : private internal::noncopyable_function_base {
11fdf7f2 103 using call_type = Ret (*)(const noncopyable_function* func, Args...);
11fdf7f2
TL
104 struct vtable {
105 const call_type call;
106 const move_type move;
107 const destroy_type destroy;
108 };
109private:
110 const vtable* _vtable;
11fdf7f2 111private:
1e59de90 112 static Ret empty_call(const noncopyable_function*, [[maybe_unused]] Args... args) {
11fdf7f2
TL
113 throw std::bad_function_call();
114 }
11fdf7f2 115
9f95a23c 116 static constexpr vtable _s_empty_vtable = {empty_call, empty_move, empty_destroy};
11fdf7f2
TL
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); }
9f95a23c 122 static Func* access(noncopyable_function_base* func) { return access(static_cast<noncopyable_function*>(func)); }
f67539c2 123 static Ret call(const noncopyable_function* func, Args... args) noexcept(Noexcept) {
11fdf7f2
TL
124 return (*access(const_cast<noncopyable_function*>(func)))(std::forward<Args>(args)...);
125 }
9f95a23c 126 static void move(noncopyable_function_base* from, noncopyable_function_base* to) {
11fdf7f2
TL
127 new (access(to)) Func(std::move(*access(from)));
128 destroy(from);
129 }
9f95a23c
TL
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) {
11fdf7f2
TL
136 access(func)->~Func();
137 }
9f95a23c
TL
138 static constexpr destroy_type select_destroy_thunk() {
139 return std::is_trivially_destructible<Func>::value ? trivial_direct_destroy : destroy;
140 }
11fdf7f2
TL
141 static void initialize(Func&& from, noncopyable_function* to) {
142 new (access(to)) Func(std::move(from));
143 }
9f95a23c 144 static constexpr vtable make_vtable() { return { call, select_move_thunk(), select_destroy_thunk() }; }
11fdf7f2
TL
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); }
9f95a23c 151 static Func* access(noncopyable_function_base* func) { return access(static_cast<noncopyable_function*>(func)); }
f67539c2 152 static Ret call(const noncopyable_function* func, Args... args) noexcept(Noexcept) {
11fdf7f2
TL
153 return (*access(const_cast<noncopyable_function*>(func)))(std::forward<Args>(args)...);
154 }
9f95a23c 155 static void destroy(noncopyable_function_base* func) {
11fdf7f2
TL
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>()> {};
175public:
176 noncopyable_function() noexcept : _vtable(&_s_empty_vtable) {}
177 template <typename Func>
1e59de90 178 SEASTAR_CONCEPT( requires std::is_invocable_r_v<Ret, Func, Args...> )
11fdf7f2 179 noncopyable_function(Func func) {
f67539c2 180 static_assert(!Noexcept || noexcept(std::declval<Func>()(std::declval<Args>()...)));
11fdf7f2
TL
181 vtable_for<Func>::initialize(std::move(func), this);
182 _vtable = &vtable_for<Func>::s_vtable;
183 }
184 template <typename Object, typename... AllButFirstArg>
f67539c2 185 noncopyable_function(Ret (Object::*member)(AllButFirstArg...) noexcept(Noexcept)) : noncopyable_function(std::mem_fn(member)) {}
11fdf7f2 186 template <typename Object, typename... AllButFirstArg>
f67539c2 187 noncopyable_function(Ret (Object::*member)(AllButFirstArg...) const noexcept(Noexcept)) : noncopyable_function(std::mem_fn(member)) {}
11fdf7f2
TL
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
f67539c2
TL
208 Ret operator()(Args... args) const noexcept(Noexcept) {
209 static_assert(!Noexcept || internal::is_nothrow_if_object<Args...>::value);
11fdf7f2
TL
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
f67539c2
TL
219template <typename Ret, typename... Args, bool Noexcept>
220constexpr typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::_s_empty_vtable;
11fdf7f2 221
f67539c2 222template <typename Ret, typename... Args, bool Noexcept>
11fdf7f2 223template <typename Func>
f67539c2
TL
224const 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();
11fdf7f2
TL
226
227
f67539c2 228template <typename Ret, typename... Args, bool Noexcept>
11fdf7f2 229template <typename Func>
f67539c2
TL
230const 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();
11fdf7f2
TL
232
233}
234