]>
Commit | Line | Data |
---|---|---|
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 | ||
30 | namespace seastar { | |
31 | ||
32 | template <typename Signature> | |
33 | class noncopyable_function; | |
34 | ||
9f95a23c TL |
35 | namespace internal { |
36 | ||
37 | class noncopyable_function_base { | |
38 | private: | |
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 | ||
75 | private: | |
76 | storage _storage; | |
77 | ||
78 | template <typename Signature> | |
79 | friend class seastar::noncopyable_function; | |
80 | }; | |
81 | ||
f67539c2 TL |
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 | ||
9f95a23c TL |
97 | } |
98 | ||
99 | /// A clone of \c std::function, but only invokes the move constructor | |
100 | /// of the contained function. | |
f67539c2 TL |
101 | template <typename Ret, typename... Args, bool Noexcept> |
102 | class 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 | }; | |
109 | private: | |
110 | const vtable* _vtable; | |
11fdf7f2 | 111 | private: |
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>()> {}; | |
175 | public: | |
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 |
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; | |
11fdf7f2 | 221 | |
f67539c2 | 222 | template <typename Ret, typename... Args, bool Noexcept> |
11fdf7f2 | 223 | template <typename Func> |
f67539c2 TL |
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(); | |
11fdf7f2 TL |
226 | |
227 | ||
f67539c2 | 228 | template <typename Ret, typename... Args, bool Noexcept> |
11fdf7f2 | 229 | template <typename Func> |
f67539c2 TL |
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(); | |
11fdf7f2 TL |
232 | |
233 | } | |
234 |