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