blob: c1f59458b9b234bb4a4c6b284321c659ed253c22 [file] [log] [blame]
Karl Wiberg3d452cf2020-09-11 14:09:461/*
2 * Copyright 2020 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Karl Wiberg70026f92020-09-18 08:03:1611#ifndef RTC_BASE_UNTYPED_FUNCTION_H_
12#define RTC_BASE_UNTYPED_FUNCTION_H_
Karl Wiberg3d452cf2020-09-11 14:09:4613
Karl Wibergd2c69672020-09-29 11:55:1314#include <cstddef>
15#include <cstring>
Karl Wiberg3d452cf2020-09-11 14:09:4616#include <memory>
17#include <type_traits>
18#include <utility>
19
20#include "rtc_base/system/assume.h"
21
22namespace webrtc {
23namespace webrtc_function_impl {
24
25using FunVoid = void();
26
Karl Wiberg84ba18a2020-10-06 10:10:5927// Inline storage size is this many machine words.
28enum : size_t { kInlineStorageWords = 4 };
29
Karl Wiberg3d452cf2020-09-11 14:09:4630union VoidUnion {
31 void* void_ptr;
32 FunVoid* fun_ptr;
Karl Wiberg84ba18a2020-10-06 10:10:5933 typename std::aligned_storage<kInlineStorageWords * sizeof(uintptr_t)>::type
34 inline_storage;
Karl Wiberg3d452cf2020-09-11 14:09:4635};
36
Karl Wibergd2c69672020-09-29 11:55:1337// Returns the number of elements of the `inline_storage` array required to
38// store an object of type T.
39template <typename T>
40constexpr size_t InlineStorageSize() {
41 // sizeof(T) / sizeof(uintptr_t), but rounded up.
42 return (sizeof(T) + sizeof(uintptr_t) - 1) / sizeof(uintptr_t);
43}
44
Karl Wiberg3d452cf2020-09-11 14:09:4645template <typename T>
46struct CallHelpers;
47template <typename RetT, typename... ArgT>
48struct CallHelpers<RetT(ArgT...)> {
Karl Wiberg804db2d2020-09-15 09:07:1049 // Return type of the three helpers below.
Karl Wiberg3d452cf2020-09-11 14:09:4650 using return_type = RetT;
Karl Wiberg804db2d2020-09-15 09:07:1051 // Complete function type of the three helpers below.
52 using function_type = RetT(VoidUnion*, ArgT...);
53 // Helper for calling the `void_ptr` case of VoidUnion.
Karl Wiberg3d452cf2020-09-11 14:09:4654 template <typename F>
55 static RetT CallVoidPtr(VoidUnion* vu, ArgT... args) {
56 return (*static_cast<F*>(vu->void_ptr))(std::forward<ArgT>(args)...);
57 }
Karl Wiberg804db2d2020-09-15 09:07:1058 // Helper for calling the `fun_ptr` case of VoidUnion.
Karl Wiberg3d452cf2020-09-11 14:09:4659 static RetT CallFunPtr(VoidUnion* vu, ArgT... args) {
60 return (reinterpret_cast<RetT (*)(ArgT...)>(vu->fun_ptr))(
61 std::forward<ArgT>(args)...);
62 }
Karl Wiberg804db2d2020-09-15 09:07:1063 // Helper for calling the `inline_storage` case of VoidUnion.
Karl Wiberg3d452cf2020-09-11 14:09:4664 template <typename F>
65 static RetT CallInlineStorage(VoidUnion* vu, ArgT... args) {
66 return (*reinterpret_cast<F*>(&vu->inline_storage))(
67 std::forward<ArgT>(args)...);
68 }
Karl Wiberg3d452cf2020-09-11 14:09:4669};
70
71} // namespace webrtc_function_impl
72
73// A class that holds (and owns) any callable. The same function call signature
74// must be provided when constructing and calling the object.
75//
76// The point of not having the call signature as a class template parameter is
77// to have one single concrete type for all signatures; this reduces binary
78// size.
79class UntypedFunction final {
80 public:
Karl Wiberg84ba18a2020-10-06 10:10:5981 // Callables of at most this size can be stored inline, if they are trivial.
82 // (Useful in tests and benchmarks; avoid using this in production code.)
83 enum : size_t {
84 kInlineStorageSize = sizeof(webrtc_function_impl::VoidUnion::inline_storage)
85 };
86 static_assert(kInlineStorageSize ==
87 webrtc_function_impl::kInlineStorageWords *
88 sizeof(uintptr_t),
89 "");
90
Karl Wibergd2c69672020-09-29 11:55:1391 // The *UntypedFunctionArgs structs are used to transfer arguments from
92 // PrepareArgs() to Create(). They are trivial, but may own heap allocations,
93 // so make sure to pass them to Create() exactly once!
94 //
95 // The point of doing Create(PrepareArgs(foo)) instead of just Create(foo) is
96 // to separate the code that has to be inlined (PrepareArgs) from the code
97 // that can be noninlined (Create); the *UntypedFunctionArgs types are
98 // designed to efficiently carry the required information from one to the
99 // other.
100 template <size_t N>
101 struct TrivialUntypedFunctionArgs {
Karl Wiberg84ba18a2020-10-06 10:10:59102 static_assert(N >= 1, "");
103 static_assert(N <= webrtc_function_impl::kInlineStorageWords, "");
Karl Wibergd2c69672020-09-29 11:55:13104 // We use an uintptr_t array here instead of std::aligned_storage, because
105 // the former can be efficiently passed in registers when using
106 // TrivialUntypedFunctionArgs as a function argument. (We can't do the same
107 // in VoidUnion, because std::aligned_storage but not uintptr_t can be
108 // legally reinterpret_casted to arbitrary types.
109 // TrivialUntypedFunctionArgs, on the other hand, only needs to handle
110 // placement new and memcpy.)
111 alignas(std::max_align_t) uintptr_t inline_storage[N];
112 webrtc_function_impl::FunVoid* call;
113 };
114 struct NontrivialUntypedFunctionArgs {
115 void* void_ptr;
116 webrtc_function_impl::FunVoid* call;
117 void (*del)(webrtc_function_impl::VoidUnion*);
118 };
119 struct FunctionPointerUntypedFunctionArgs {
120 webrtc_function_impl::FunVoid* fun_ptr;
121 webrtc_function_impl::FunVoid* call;
122 };
123
124 // Create function for lambdas and other callables that are trivial and small;
125 // it accepts every type of argument except those noted in its enable_if call.
Karl Wiberg3d452cf2020-09-11 14:09:46126 template <
127 typename Signature,
128 typename F,
Karl Wibergd2c69672020-09-29 11:55:13129 typename F_deref = typename std::remove_reference<F>::type,
Karl Wiberg3d452cf2020-09-11 14:09:46130 typename std::enable_if<
131 // Not for function pointers; we have another overload for that below.
Karl Wibergd2c69672020-09-29 11:55:13132 !std::is_function<
133 typename std::remove_pointer<F_deref>::type>::value &&
Karl Wiberg3d452cf2020-09-11 14:09:46134
Karl Wibergd2c69672020-09-29 11:55:13135 // Not for nullptr; we have a constructor for that below.
Karl Wiberg3d452cf2020-09-11 14:09:46136 !std::is_same<std::nullptr_t,
137 typename std::remove_cv<F>::type>::value &&
138
Karl Wibergd2c69672020-09-29 11:55:13139 // Not for UntypedFunction objects; use move construction or
140 // assignment.
Karl Wiberg3d452cf2020-09-11 14:09:46141 !std::is_same<UntypedFunction,
Karl Wibergd2c69672020-09-29 11:55:13142 typename std::remove_cv<F_deref>::type>::value &&
143
Karl Wiberg84ba18a2020-10-06 10:10:59144 // Only for trivial callables that will fit in inline storage.
Karl Wibergd2c69672020-09-29 11:55:13145 std::is_trivially_move_constructible<F_deref>::value &&
146 std::is_trivially_destructible<F_deref>::value &&
Karl Wiberg84ba18a2020-10-06 10:10:59147 sizeof(F_deref) <= kInlineStorageSize>::type* = nullptr,
Karl Wibergd2c69672020-09-29 11:55:13148 size_t InlineSize = webrtc_function_impl::InlineStorageSize<F_deref>()>
149 static TrivialUntypedFunctionArgs<InlineSize> PrepareArgs(F&& f) {
150 // The callable is trivial and small enough, so we just store its bytes
151 // in the inline storage.
152 TrivialUntypedFunctionArgs<InlineSize> args;
153 new (&args.inline_storage) F_deref(std::forward<F>(f));
154 args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>(
155 webrtc_function_impl::CallHelpers<
156 Signature>::template CallInlineStorage<F_deref>);
157 return args;
158 }
159 template <size_t InlineSize>
160 static UntypedFunction Create(TrivialUntypedFunctionArgs<InlineSize> args) {
161 webrtc_function_impl::VoidUnion vu;
162 std::memcpy(&vu.inline_storage, args.inline_storage,
163 sizeof(args.inline_storage));
164 return UntypedFunction(vu, args.call, nullptr);
165 }
166
167 // Create function for lambdas and other callables that are nontrivial or
168 // large; it accepts every type of argument except those noted in its
169 // enable_if call.
Karl Wiberg84ba18a2020-10-06 10:10:59170 template <typename Signature,
171 typename F,
172 typename F_deref = typename std::remove_reference<F>::type,
173 typename std::enable_if<
174 // Not for function pointers; we have another overload for that
175 // below.
176 !std::is_function<
177 typename std::remove_pointer<F_deref>::type>::value &&
Karl Wibergd2c69672020-09-29 11:55:13178
Karl Wiberg84ba18a2020-10-06 10:10:59179 // Not for nullptr; we have a constructor for that below.
180 !std::is_same<std::nullptr_t,
181 typename std::remove_cv<F>::type>::value &&
Karl Wibergd2c69672020-09-29 11:55:13182
Karl Wiberg84ba18a2020-10-06 10:10:59183 // Not for UntypedFunction objects; use move construction or
184 // assignment.
185 !std::is_same<UntypedFunction,
186 typename std::remove_cv<F_deref>::type>::value &&
Karl Wibergd2c69672020-09-29 11:55:13187
Karl Wiberg84ba18a2020-10-06 10:10:59188 // Only for nontrivial callables, or callables that won't fit in
189 // inline storage.
190 !(std::is_trivially_move_constructible<F_deref>::value &&
191 std::is_trivially_destructible<F_deref>::value &&
192 sizeof(F_deref) <= kInlineStorageSize)>::type* = nullptr>
Karl Wibergd2c69672020-09-29 11:55:13193 static NontrivialUntypedFunctionArgs PrepareArgs(F&& f) {
194 // The callable is either nontrivial or too large, so we can't keep it
195 // in the inline storage; use the heap instead.
196 NontrivialUntypedFunctionArgs args;
197 args.void_ptr = new F_deref(std::forward<F>(f));
198 args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>(
199 webrtc_function_impl::CallHelpers<Signature>::template CallVoidPtr<
200 F_deref>);
201 args.del = static_cast<void (*)(webrtc_function_impl::VoidUnion*)>(
202 [](webrtc_function_impl::VoidUnion* vu) {
203 // Assuming that this pointer isn't null allows the
204 // compiler to eliminate a null check in the (inlined)
205 // delete operation.
206 RTC_ASSUME(vu->void_ptr != nullptr);
207 delete reinterpret_cast<F_deref*>(vu->void_ptr);
208 });
209 return args;
210 }
211 static UntypedFunction Create(NontrivialUntypedFunctionArgs args) {
212 webrtc_function_impl::VoidUnion vu;
213 vu.void_ptr = args.void_ptr;
214 return UntypedFunction(vu, args.call, args.del);
Karl Wiberg3d452cf2020-09-11 14:09:46215 }
216
217 // Create function that accepts function pointers. If the argument is null,
218 // the result is an empty UntypedFunction.
219 template <typename Signature>
Karl Wibergd2c69672020-09-29 11:55:13220 static FunctionPointerUntypedFunctionArgs PrepareArgs(Signature* f) {
221 FunctionPointerUntypedFunctionArgs args;
222 args.fun_ptr = reinterpret_cast<webrtc_function_impl::FunVoid*>(f);
223 args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>(
224 webrtc_function_impl::CallHelpers<Signature>::CallFunPtr);
225 return args;
226 }
227 static UntypedFunction Create(FunctionPointerUntypedFunctionArgs args) {
Karl Wiberg78e9acd2020-09-15 09:06:34228 webrtc_function_impl::VoidUnion vu;
Karl Wibergd2c69672020-09-29 11:55:13229 vu.fun_ptr = args.fun_ptr;
230 return UntypedFunction(vu, args.fun_ptr == nullptr ? nullptr : args.call,
231 nullptr);
232 }
233
234 // Prepares arguments and creates an UntypedFunction in one go.
235 template <typename Signature, typename F>
236 static UntypedFunction Create(F&& f) {
237 return Create(PrepareArgs<Signature>(std::forward<F>(f)));
Karl Wiberg3d452cf2020-09-11 14:09:46238 }
239
240 // Default constructor. Creates an empty UntypedFunction.
241 UntypedFunction() : call_(nullptr), delete_(nullptr) {}
242
Karl Wiberg78e9acd2020-09-15 09:06:34243 // Nullptr constructor and assignment. Creates an empty UntypedFunction.
244 UntypedFunction(std::nullptr_t) // NOLINT(runtime/explicit)
245 : call_(nullptr), delete_(nullptr) {}
246 UntypedFunction& operator=(std::nullptr_t) {
247 call_ = nullptr;
248 if (delete_) {
249 delete_(&f_);
250 delete_ = nullptr;
251 }
252 return *this;
253 }
254
Karl Wiberg3d452cf2020-09-11 14:09:46255 // Not copyable.
256 UntypedFunction(const UntypedFunction&) = delete;
257 UntypedFunction& operator=(const UntypedFunction&) = delete;
258
259 // Move construction and assignment.
260 UntypedFunction(UntypedFunction&& other)
261 : f_(other.f_), call_(other.call_), delete_(other.delete_) {
262 other.delete_ = nullptr;
263 }
264 UntypedFunction& operator=(UntypedFunction&& other) {
Karl Wiberg78e9acd2020-09-15 09:06:34265 if (delete_) {
266 delete_(&f_);
267 }
Karl Wiberg3d452cf2020-09-11 14:09:46268 f_ = other.f_;
269 call_ = other.call_;
270 delete_ = other.delete_;
271 other.delete_ = nullptr;
272 return *this;
273 }
274
275 ~UntypedFunction() {
276 if (delete_) {
277 delete_(&f_);
278 }
279 }
280
281 friend void swap(UntypedFunction& a, UntypedFunction& b) {
282 using std::swap;
283 swap(a.f_, b.f_);
284 swap(a.call_, b.call_);
285 swap(a.delete_, b.delete_);
286 }
287
288 // Returns true if we have a function, false if we don't (i.e., we're null).
289 explicit operator bool() const { return call_ != nullptr; }
290
291 template <typename Signature, typename... ArgT>
292 typename webrtc_function_impl::CallHelpers<Signature>::return_type Call(
Karl Wiberg78e9acd2020-09-15 09:06:34293 ArgT&&... args) {
Karl Wiberg804db2d2020-09-15 09:07:10294 return reinterpret_cast<
295 typename webrtc_function_impl::CallHelpers<Signature>::function_type*>(
296 call_)(&f_, std::forward<ArgT>(args)...);
Karl Wiberg3d452cf2020-09-11 14:09:46297 }
298
299 // Returns true iff we don't need to call a destructor. This is guaranteed
300 // to hold for a moved-from object.
301 bool IsTriviallyDestructible() { return delete_ == nullptr; }
302
303 private:
304 UntypedFunction(webrtc_function_impl::VoidUnion f,
305 webrtc_function_impl::FunVoid* call,
306 void (*del)(webrtc_function_impl::VoidUnion*))
307 : f_(f), call_(call), delete_(del) {}
308
309 // The callable thing, or a pointer to it.
310 webrtc_function_impl::VoidUnion f_;
311
312 // Pointer to a dispatch function that knows the type of the callable thing
313 // that's stored in f_, and how to call it. An UntypedFunction object is empty
314 // (null) iff call_ is null.
315 webrtc_function_impl::FunVoid* call_;
316
317 // Pointer to a function that knows how to delete the callable thing that's
318 // stored in f_. Null if `f_` is trivially deletable.
319 void (*delete_)(webrtc_function_impl::VoidUnion*);
320};
321
322} // namespace webrtc
323
Karl Wiberg70026f92020-09-18 08:03:16324#endif // RTC_BASE_UNTYPED_FUNCTION_H_