]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/util/noncopyable_function.hh
update source to Ceph Pacific 16.2.2
[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
TL
24#include <seastar/util/used_size.hh>
25
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
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
67private:
68 storage _storage;
69
70 template <typename Signature>
71 friend class seastar::noncopyable_function;
72};
73
f67539c2
TL
74template<typename FirstArg = void, typename... RemainingArgs>
75struct is_nothrow_if_object {
76 static constexpr bool value = is_nothrow_if_object<FirstArg>::value && is_nothrow_if_object<RemainingArgs...>::value;
77};
78
79template<typename Arg>
80struct is_nothrow_if_object<Arg> {
81 static constexpr bool value = !std::is_object<Arg>::value || std::is_nothrow_move_constructible<Arg>::value;
82};
83
84template<>
85struct 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
93template <typename Ret, typename... Args, bool Noexcept>
94class 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 };
101private:
102 const vtable* _vtable;
11fdf7f2
TL
103private:
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>()> {};
167public:
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
210template <typename Ret, typename... Args, bool Noexcept>
211constexpr typename noncopyable_function<Ret (Args...) noexcept(Noexcept)>::vtable noncopyable_function<Ret (Args...) noexcept(Noexcept)>::_s_empty_vtable;
11fdf7f2 212
f67539c2 213template <typename Ret, typename... Args, bool Noexcept>
11fdf7f2 214template <typename Func>
f67539c2
TL
215const 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 219template <typename Ret, typename... Args, bool Noexcept>
11fdf7f2 220template <typename Func>
f67539c2
TL
221const 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