blob: b7e1de15510cfc9266b3c79bbb9c102521c89f70 [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;
Victor Hugo Vianna Silvaa6f35492025-02-03 10:43:1433 // std::max_align_t satisfies alignment requirements for every type.
34 alignas(std::max_align_t) char inline_storage[kInlineStorageWords *
35 sizeof(uintptr_t)];
Karl Wiberg3d452cf2020-09-11 14:09:4636};
37
Karl Wibergd2c69672020-09-29 11:55:1338// Returns the number of elements of the `inline_storage` array required to
39// store an object of type T.
40template <typename T>
41constexpr size_t InlineStorageSize() {
42 // sizeof(T) / sizeof(uintptr_t), but rounded up.
43 return (sizeof(T) + sizeof(uintptr_t) - 1) / sizeof(uintptr_t);
44}
45
Karl Wiberg3d452cf2020-09-11 14:09:4646template <typename T>
47struct CallHelpers;
48template <typename RetT, typename... ArgT>
49struct CallHelpers<RetT(ArgT...)> {
Karl Wiberg804db2d2020-09-15 09:07:1050 // Return type of the three helpers below.
Karl Wiberg3d452cf2020-09-11 14:09:4651 using return_type = RetT;
Karl Wiberg804db2d2020-09-15 09:07:1052 // Complete function type of the three helpers below.
53 using function_type = RetT(VoidUnion*, ArgT...);
54 // Helper for calling the `void_ptr` case of VoidUnion.
Karl Wiberg3d452cf2020-09-11 14:09:4655 template <typename F>
56 static RetT CallVoidPtr(VoidUnion* vu, ArgT... args) {
57 return (*static_cast<F*>(vu->void_ptr))(std::forward<ArgT>(args)...);
58 }
Karl Wiberg804db2d2020-09-15 09:07:1059 // Helper for calling the `fun_ptr` case of VoidUnion.
Karl Wiberg3d452cf2020-09-11 14:09:4660 static RetT CallFunPtr(VoidUnion* vu, ArgT... args) {
61 return (reinterpret_cast<RetT (*)(ArgT...)>(vu->fun_ptr))(
62 std::forward<ArgT>(args)...);
63 }
Karl Wiberg804db2d2020-09-15 09:07:1064 // Helper for calling the `inline_storage` case of VoidUnion.
Karl Wiberg3d452cf2020-09-11 14:09:4665 template <typename F>
66 static RetT CallInlineStorage(VoidUnion* vu, ArgT... args) {
67 return (*reinterpret_cast<F*>(&vu->inline_storage))(
68 std::forward<ArgT>(args)...);
69 }
Karl Wiberg3d452cf2020-09-11 14:09:4670};
71
72} // namespace webrtc_function_impl
73
74// A class that holds (and owns) any callable. The same function call signature
75// must be provided when constructing and calling the object.
76//
77// The point of not having the call signature as a class template parameter is
78// to have one single concrete type for all signatures; this reduces binary
79// size.
80class UntypedFunction final {
81 public:
Karl Wiberg84ba18a2020-10-06 10:10:5982 // Callables of at most this size can be stored inline, if they are trivial.
83 // (Useful in tests and benchmarks; avoid using this in production code.)
84 enum : size_t {
85 kInlineStorageSize = sizeof(webrtc_function_impl::VoidUnion::inline_storage)
86 };
87 static_assert(kInlineStorageSize ==
88 webrtc_function_impl::kInlineStorageWords *
89 sizeof(uintptr_t),
90 "");
91
Karl Wibergd2c69672020-09-29 11:55:1392 // The *UntypedFunctionArgs structs are used to transfer arguments from
93 // PrepareArgs() to Create(). They are trivial, but may own heap allocations,
94 // so make sure to pass them to Create() exactly once!
95 //
96 // The point of doing Create(PrepareArgs(foo)) instead of just Create(foo) is
97 // to separate the code that has to be inlined (PrepareArgs) from the code
98 // that can be noninlined (Create); the *UntypedFunctionArgs types are
99 // designed to efficiently carry the required information from one to the
100 // other.
101 template <size_t N>
102 struct TrivialUntypedFunctionArgs {
Karl Wiberg84ba18a2020-10-06 10:10:59103 static_assert(N >= 1, "");
104 static_assert(N <= webrtc_function_impl::kInlineStorageWords, "");
Karl Wibergd2c69672020-09-29 11:55:13105 // We use an uintptr_t array here instead of std::aligned_storage, because
106 // the former can be efficiently passed in registers when using
107 // TrivialUntypedFunctionArgs as a function argument. (We can't do the same
108 // in VoidUnion, because std::aligned_storage but not uintptr_t can be
109 // legally reinterpret_casted to arbitrary types.
110 // TrivialUntypedFunctionArgs, on the other hand, only needs to handle
111 // placement new and memcpy.)
112 alignas(std::max_align_t) uintptr_t inline_storage[N];
113 webrtc_function_impl::FunVoid* call;
114 };
115 struct NontrivialUntypedFunctionArgs {
116 void* void_ptr;
117 webrtc_function_impl::FunVoid* call;
118 void (*del)(webrtc_function_impl::VoidUnion*);
119 };
120 struct FunctionPointerUntypedFunctionArgs {
121 webrtc_function_impl::FunVoid* fun_ptr;
122 webrtc_function_impl::FunVoid* call;
123 };
124
125 // Create function for lambdas and other callables that are trivial and small;
126 // it accepts every type of argument except those noted in its enable_if call.
Karl Wiberg3d452cf2020-09-11 14:09:46127 template <
128 typename Signature,
129 typename F,
Karl Wibergd2c69672020-09-29 11:55:13130 typename F_deref = typename std::remove_reference<F>::type,
Karl Wiberg3d452cf2020-09-11 14:09:46131 typename std::enable_if<
132 // Not for function pointers; we have another overload for that below.
Karl Wibergd2c69672020-09-29 11:55:13133 !std::is_function<
134 typename std::remove_pointer<F_deref>::type>::value &&
Karl Wiberg3d452cf2020-09-11 14:09:46135
Karl Wibergd2c69672020-09-29 11:55:13136 // Not for nullptr; we have a constructor for that below.
Karl Wiberg3d452cf2020-09-11 14:09:46137 !std::is_same<std::nullptr_t,
138 typename std::remove_cv<F>::type>::value &&
139
Karl Wibergd2c69672020-09-29 11:55:13140 // Not for UntypedFunction objects; use move construction or
141 // assignment.
Karl Wiberg3d452cf2020-09-11 14:09:46142 !std::is_same<UntypedFunction,
Karl Wibergd2c69672020-09-29 11:55:13143 typename std::remove_cv<F_deref>::type>::value &&
144
Karl Wiberg84ba18a2020-10-06 10:10:59145 // Only for trivial callables that will fit in inline storage.
Karl Wibergd2c69672020-09-29 11:55:13146 std::is_trivially_move_constructible<F_deref>::value &&
147 std::is_trivially_destructible<F_deref>::value &&
Karl Wiberg84ba18a2020-10-06 10:10:59148 sizeof(F_deref) <= kInlineStorageSize>::type* = nullptr,
Karl Wibergd2c69672020-09-29 11:55:13149 size_t InlineSize = webrtc_function_impl::InlineStorageSize<F_deref>()>
150 static TrivialUntypedFunctionArgs<InlineSize> PrepareArgs(F&& f) {
151 // The callable is trivial and small enough, so we just store its bytes
152 // in the inline storage.
153 TrivialUntypedFunctionArgs<InlineSize> args;
154 new (&args.inline_storage) F_deref(std::forward<F>(f));
155 args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>(
156 webrtc_function_impl::CallHelpers<
157 Signature>::template CallInlineStorage<F_deref>);
158 return args;
159 }
160 template <size_t InlineSize>
161 static UntypedFunction Create(TrivialUntypedFunctionArgs<InlineSize> args) {
162 webrtc_function_impl::VoidUnion vu;
163 std::memcpy(&vu.inline_storage, args.inline_storage,
164 sizeof(args.inline_storage));
165 return UntypedFunction(vu, args.call, nullptr);
166 }
167
168 // Create function for lambdas and other callables that are nontrivial or
169 // large; it accepts every type of argument except those noted in its
170 // enable_if call.
Karl Wiberg84ba18a2020-10-06 10:10:59171 template <typename Signature,
172 typename F,
173 typename F_deref = typename std::remove_reference<F>::type,
174 typename std::enable_if<
175 // Not for function pointers; we have another overload for that
176 // below.
177 !std::is_function<
178 typename std::remove_pointer<F_deref>::type>::value &&
Karl Wibergd2c69672020-09-29 11:55:13179
Karl Wiberg84ba18a2020-10-06 10:10:59180 // Not for nullptr; we have a constructor for that below.
181 !std::is_same<std::nullptr_t,
182 typename std::remove_cv<F>::type>::value &&
Karl Wibergd2c69672020-09-29 11:55:13183
Karl Wiberg84ba18a2020-10-06 10:10:59184 // Not for UntypedFunction objects; use move construction or
185 // assignment.
186 !std::is_same<UntypedFunction,
187 typename std::remove_cv<F_deref>::type>::value &&
Karl Wibergd2c69672020-09-29 11:55:13188
Karl Wiberg84ba18a2020-10-06 10:10:59189 // Only for nontrivial callables, or callables that won't fit in
190 // inline storage.
191 !(std::is_trivially_move_constructible<F_deref>::value &&
192 std::is_trivially_destructible<F_deref>::value &&
193 sizeof(F_deref) <= kInlineStorageSize)>::type* = nullptr>
Karl Wibergd2c69672020-09-29 11:55:13194 static NontrivialUntypedFunctionArgs PrepareArgs(F&& f) {
195 // The callable is either nontrivial or too large, so we can't keep it
196 // in the inline storage; use the heap instead.
197 NontrivialUntypedFunctionArgs args;
198 args.void_ptr = new F_deref(std::forward<F>(f));
199 args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>(
200 webrtc_function_impl::CallHelpers<Signature>::template CallVoidPtr<
201 F_deref>);
202 args.del = static_cast<void (*)(webrtc_function_impl::VoidUnion*)>(
203 [](webrtc_function_impl::VoidUnion* vu) {
204 // Assuming that this pointer isn't null allows the
205 // compiler to eliminate a null check in the (inlined)
206 // delete operation.
207 RTC_ASSUME(vu->void_ptr != nullptr);
208 delete reinterpret_cast<F_deref*>(vu->void_ptr);
209 });
210 return args;
211 }
212 static UntypedFunction Create(NontrivialUntypedFunctionArgs args) {
213 webrtc_function_impl::VoidUnion vu;
214 vu.void_ptr = args.void_ptr;
215 return UntypedFunction(vu, args.call, args.del);
Karl Wiberg3d452cf2020-09-11 14:09:46216 }
217
218 // Create function that accepts function pointers. If the argument is null,
219 // the result is an empty UntypedFunction.
220 template <typename Signature>
Karl Wibergd2c69672020-09-29 11:55:13221 static FunctionPointerUntypedFunctionArgs PrepareArgs(Signature* f) {
222 FunctionPointerUntypedFunctionArgs args;
223 args.fun_ptr = reinterpret_cast<webrtc_function_impl::FunVoid*>(f);
224 args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>(
225 webrtc_function_impl::CallHelpers<Signature>::CallFunPtr);
226 return args;
227 }
228 static UntypedFunction Create(FunctionPointerUntypedFunctionArgs args) {
Karl Wiberg78e9acd2020-09-15 09:06:34229 webrtc_function_impl::VoidUnion vu;
Karl Wibergd2c69672020-09-29 11:55:13230 vu.fun_ptr = args.fun_ptr;
231 return UntypedFunction(vu, args.fun_ptr == nullptr ? nullptr : args.call,
232 nullptr);
233 }
234
235 // Prepares arguments and creates an UntypedFunction in one go.
236 template <typename Signature, typename F>
237 static UntypedFunction Create(F&& f) {
238 return Create(PrepareArgs<Signature>(std::forward<F>(f)));
Karl Wiberg3d452cf2020-09-11 14:09:46239 }
240
241 // Default constructor. Creates an empty UntypedFunction.
242 UntypedFunction() : call_(nullptr), delete_(nullptr) {}
243
Karl Wiberg78e9acd2020-09-15 09:06:34244 // Nullptr constructor and assignment. Creates an empty UntypedFunction.
245 UntypedFunction(std::nullptr_t) // NOLINT(runtime/explicit)
246 : call_(nullptr), delete_(nullptr) {}
247 UntypedFunction& operator=(std::nullptr_t) {
248 call_ = nullptr;
249 if (delete_) {
250 delete_(&f_);
251 delete_ = nullptr;
252 }
253 return *this;
254 }
255
Karl Wiberg3d452cf2020-09-11 14:09:46256 // Not copyable.
257 UntypedFunction(const UntypedFunction&) = delete;
258 UntypedFunction& operator=(const UntypedFunction&) = delete;
259
260 // Move construction and assignment.
261 UntypedFunction(UntypedFunction&& other)
262 : f_(other.f_), call_(other.call_), delete_(other.delete_) {
263 other.delete_ = nullptr;
264 }
265 UntypedFunction& operator=(UntypedFunction&& other) {
Karl Wiberg78e9acd2020-09-15 09:06:34266 if (delete_) {
267 delete_(&f_);
268 }
Karl Wiberg3d452cf2020-09-11 14:09:46269 f_ = other.f_;
270 call_ = other.call_;
271 delete_ = other.delete_;
272 other.delete_ = nullptr;
273 return *this;
274 }
275
276 ~UntypedFunction() {
277 if (delete_) {
278 delete_(&f_);
279 }
280 }
281
282 friend void swap(UntypedFunction& a, UntypedFunction& b) {
283 using std::swap;
284 swap(a.f_, b.f_);
285 swap(a.call_, b.call_);
286 swap(a.delete_, b.delete_);
287 }
288
289 // Returns true if we have a function, false if we don't (i.e., we're null).
290 explicit operator bool() const { return call_ != nullptr; }
291
292 template <typename Signature, typename... ArgT>
293 typename webrtc_function_impl::CallHelpers<Signature>::return_type Call(
Karl Wiberg78e9acd2020-09-15 09:06:34294 ArgT&&... args) {
Karl Wiberg804db2d2020-09-15 09:07:10295 return reinterpret_cast<
296 typename webrtc_function_impl::CallHelpers<Signature>::function_type*>(
297 call_)(&f_, std::forward<ArgT>(args)...);
Karl Wiberg3d452cf2020-09-11 14:09:46298 }
299
300 // Returns true iff we don't need to call a destructor. This is guaranteed
301 // to hold for a moved-from object.
302 bool IsTriviallyDestructible() { return delete_ == nullptr; }
303
304 private:
305 UntypedFunction(webrtc_function_impl::VoidUnion f,
306 webrtc_function_impl::FunVoid* call,
307 void (*del)(webrtc_function_impl::VoidUnion*))
308 : f_(f), call_(call), delete_(del) {}
309
310 // The callable thing, or a pointer to it.
311 webrtc_function_impl::VoidUnion f_;
312
313 // Pointer to a dispatch function that knows the type of the callable thing
314 // that's stored in f_, and how to call it. An UntypedFunction object is empty
315 // (null) iff call_ is null.
316 webrtc_function_impl::FunVoid* call_;
317
318 // Pointer to a function that knows how to delete the callable thing that's
319 // stored in f_. Null if `f_` is trivially deletable.
320 void (*delete_)(webrtc_function_impl::VoidUnion*);
321};
322
323} // namespace webrtc
324
Karl Wiberg70026f92020-09-18 08:03:16325#endif // RTC_BASE_UNTYPED_FUNCTION_H_