]>
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 TL |
24 | #include <seastar/util/used_size.hh> |
25 | ||
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 | ||
48 | static void empty_move(noncopyable_function_base* from, noncopyable_function_base* to) {} | |
49 | static void empty_destroy(noncopyable_function_base* func) {} | |
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 | // Avoid including <algorithm> just for this | |
59 | for (unsigned i = 0; i != N; ++i) { | |
60 | to->_storage.direct[i] = from->_storage.direct[i]; | |
61 | } | |
62 | } | |
63 | ||
64 | static void trivial_direct_destroy(noncopyable_function_base* func) { | |
65 | } | |
66 | ||
67 | private: | |
68 | storage _storage; | |
69 | ||
70 | template <typename Signature> | |
71 | friend class seastar::noncopyable_function; | |
72 | }; | |
73 | ||
f67539c2 TL |
74 | template<typename FirstArg = void, typename... RemainingArgs> |
75 | struct is_nothrow_if_object { | |
76 | static constexpr bool value = is_nothrow_if_object<FirstArg>::value && is_nothrow_if_object<RemainingArgs...>::value; | |
77 | }; | |
78 | ||
79 | template<typename Arg> | |
80 | struct is_nothrow_if_object<Arg> { | |
81 | static constexpr bool value = !std::is_object<Arg>::value || std::is_nothrow_move_constructible<Arg>::value; | |
82 | }; | |
83 | ||
84 | template<> | |
85 | struct is_nothrow_if_object<> { | |
86 | static constexpr bool value = true; | |
87 | }; | |
88 | ||
9f95a23c TL |
89 | } |
90 | ||
91 | /// A clone of \c std::function, but only invokes the move constructor | |
92 | /// of the contained function. | |
f67539c2 TL |
93 | template <typename Ret, typename... Args, bool Noexcept> |
94 | class noncopyable_function<Ret (Args...) noexcept(Noexcept)> : private internal::noncopyable_function_base { | |
11fdf7f2 | 95 | using call_type = Ret (*)(const noncopyable_function* func, Args...); |
11fdf7f2 TL |
96 | struct vtable { |
97 | const call_type call; | |
98 | const move_type move; | |
99 | const destroy_type destroy; | |
100 | }; | |
101 | private: | |
102 | const vtable* _vtable; | |
11fdf7f2 TL |
103 | private: |
104 | static Ret empty_call(const noncopyable_function* func, Args... args) { | |
105 | throw std::bad_function_call(); | |
106 | } | |
11fdf7f2 | 107 | |
9f95a23c | 108 | static constexpr vtable _s_empty_vtable = {empty_call, empty_move, empty_destroy}; |
11fdf7f2 TL |
109 | |
110 | template <typename Func> | |
111 | struct direct_vtable_for { | |
112 | static Func* access(noncopyable_function* func) { return reinterpret_cast<Func*>(func->_storage.direct); } | |
113 | static const Func* access(const noncopyable_function* func) { return reinterpret_cast<const Func*>(func->_storage.direct); } | |
9f95a23c | 114 | static Func* access(noncopyable_function_base* func) { return access(static_cast<noncopyable_function*>(func)); } |
f67539c2 | 115 | static Ret call(const noncopyable_function* func, Args... args) noexcept(Noexcept) { |
11fdf7f2 TL |
116 | return (*access(const_cast<noncopyable_function*>(func)))(std::forward<Args>(args)...); |
117 | } | |
9f95a23c | 118 | static void move(noncopyable_function_base* from, noncopyable_function_base* to) { |
11fdf7f2 TL |
119 | new (access(to)) Func(std::move(*access(from))); |
120 | destroy(from); | |
121 | } | |
9f95a23c TL |
122 | static constexpr move_type select_move_thunk() { |
123 | bool can_trivially_move = std::is_trivially_move_constructible<Func>::value | |
124 | && std::is_trivially_destructible<Func>::value; | |
125 | return can_trivially_move ? trivial_direct_move<internal::used_size<Func>::value> : move; | |
126 | } | |
127 | static void destroy(noncopyable_function_base* func) { | |
11fdf7f2 TL |
128 | access(func)->~Func(); |
129 | } | |
9f95a23c TL |
130 | static constexpr destroy_type select_destroy_thunk() { |
131 | return std::is_trivially_destructible<Func>::value ? trivial_direct_destroy : destroy; | |
132 | } | |
11fdf7f2 TL |
133 | static void initialize(Func&& from, noncopyable_function* to) { |
134 | new (access(to)) Func(std::move(from)); | |
135 | } | |
9f95a23c | 136 | static constexpr vtable make_vtable() { return { call, select_move_thunk(), select_destroy_thunk() }; } |
11fdf7f2 TL |
137 | static const vtable s_vtable; |
138 | }; | |
139 | template <typename Func> | |
140 | struct indirect_vtable_for { | |
141 | static Func* access(noncopyable_function* func) { return reinterpret_cast<Func*>(func->_storage.indirect); } | |
142 | static const Func* access(const noncopyable_function* func) { return reinterpret_cast<const Func*>(func->_storage.indirect); } | |
9f95a23c | 143 | static Func* access(noncopyable_function_base* func) { return access(static_cast<noncopyable_function*>(func)); } |
f67539c2 | 144 | static Ret call(const noncopyable_function* func, Args... args) noexcept(Noexcept) { |
11fdf7f2 TL |
145 | return (*access(const_cast<noncopyable_function*>(func)))(std::forward<Args>(args)...); |
146 | } | |
9f95a23c | 147 | static void destroy(noncopyable_function_base* func) { |
11fdf7f2 TL |
148 | delete access(func); |
149 | } | |
150 | static void initialize(Func&& from, noncopyable_function* to) { | |
151 | to->_storage.indirect = new Func(std::move(from)); | |
152 | } | |
153 | static constexpr vtable make_vtable() { return { call, indirect_move, destroy }; } | |
154 | static const vtable s_vtable; | |
155 | }; | |
156 | template <typename Func, bool Direct = true> | |
157 | struct select_vtable_for : direct_vtable_for<Func> {}; | |
158 | template <typename Func> | |
159 | struct select_vtable_for<Func, false> : indirect_vtable_for<Func> {}; | |
160 | template <typename Func> | |
161 | static constexpr bool is_direct() { | |
162 | return sizeof(Func) <= nr_direct && alignof(Func) <= alignof(storage) | |
163 | && std::is_nothrow_move_constructible<Func>::value; | |
164 | } | |
165 | template <typename Func> | |
166 | struct vtable_for : select_vtable_for<Func, is_direct<Func>()> {}; | |
167 | public: | |
168 | noncopyable_function() noexcept : _vtable(&_s_empty_vtable) {} | |
169 | template <typename Func> | |
170 | noncopyable_function(Func func) { | |
f67539c2 | 171 | static_assert(!Noexcept || noexcept(std::declval<Func>()(std::declval<Args>()...))); |
11fdf7f2 TL |
172 | vtable_for<Func>::initialize(std::move(func), this); |
173 | _vtable = &vtable_for<Func>::s_vtable; | |
174 | } | |
175 | template <typename Object, typename... AllButFirstArg> | |
f67539c2 | 176 | noncopyable_function(Ret (Object::*member)(AllButFirstArg...) noexcept(Noexcept)) : noncopyable_function(std::mem_fn(member)) {} |
11fdf7f2 | 177 | template <typename Object, typename... AllButFirstArg> |
f67539c2 | 178 | noncopyable_function(Ret (Object::*member)(AllButFirstArg...) const noexcept(Noexcept)) : noncopyable_function(std::mem_fn(member)) {} |
11fdf7f2 TL |
179 | |
180 | ~noncopyable_function() { | |
181 | _vtable->destroy(this); | |
182 | } | |
183 | ||
184 | noncopyable_function(const noncopyable_function&) = delete; | |
185 | noncopyable_function& operator=(const noncopyable_function&) = delete; | |
186 | ||
187 | noncopyable_function(noncopyable_function&& x) noexcept : _vtable(std::exchange(x._vtable, &_s_empty_vtable)) { | |
188 | _vtable->move(&x, this); | |
189 | } | |
190 | ||
191 | noncopyable_function& operator=(noncopyable_function&& x) noexcept { | |
192 | if (this != &x) { | |
193 | this->~noncopyable_function(); | |
194 | new (this) noncopyable_function(std::move(x)); | |
195 | } | |
196 | return *this; | |
197 | } | |
198 | ||
f67539c2 TL |
199 | Ret operator()(Args... args) const noexcept(Noexcept) { |
200 | static_assert(!Noexcept || internal::is_nothrow_if_object<Args...>::value); | |
11fdf7f2 TL |
201 | return _vtable->call(this, std::forward<Args>(args)...); |
202 | } | |
203 | ||
204 | explicit operator bool() const { | |
205 | return _vtable != &_s_empty_vtable; | |
206 | } | |
207 | }; | |
208 | ||
209 | ||
f67539c2 TL |
210 | template <typename Ret, typename... Args, bool Noexcept> |
211 | constexpr typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::_s_empty_vtable; | |
11fdf7f2 | 212 | |
f67539c2 | 213 | template <typename Ret, typename... Args, bool Noexcept> |
11fdf7f2 | 214 | template <typename Func> |
f67539c2 TL |
215 | const typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::direct_vtable_for<Func>::s_vtable |
216 | = noncopyable_function<Ret (Args...) noexcept(Noexcept)>::direct_vtable_for<Func>::make_vtable(); | |
11fdf7f2 TL |
217 | |
218 | ||
f67539c2 | 219 | template <typename Ret, typename... Args, bool Noexcept> |
11fdf7f2 | 220 | template <typename Func> |
f67539c2 TL |
221 | const typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::indirect_vtable_for<Func>::s_vtable |
222 | = noncopyable_function<Ret (Args...) noexcept(Noexcept)>::indirect_vtable_for<Func>::make_vtable(); | |
11fdf7f2 TL |
223 | |
224 | } | |
225 |